如果 where_in 子句在 MyBatis 3 中为空,如何跳过查询?

How can I skip query if where_in clause is empty in MyBatis 3?

select * from users where id in ()

查询如上所示。

<select id="getByIds" resultMap="BaseResultMap">
    SELECT
    <include refid="BaseColumnList"/>
    FROM users
    WHERE id IN
    <foreach item="id" collection="ids"
             open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

如果参数ids为空,Mybatis会抛出BadSqlGrammarException异常,生成类似'select * from users where id in'的查询。

如果 ids 为空,我如何跳过查询和 return 空列表?

使用测试:

<select id="getByIds" resultMap="BaseResultMap">
    SELECT
    <include refid="BaseColumnList"/>
    FROM users
<if test="ids!= null">
    WHERE id IN
    <foreach item="id" collection="ids"
             open="(" separator="," close=")">
        #{id}
    </foreach>
</if>
</select>

How can I skip query and return empty list if ids is empty?

跳过查询(不执行),只要不调用Mybatis即可。 调用代码应检查 ids 是否为空:

return null == ids || ids.isEmpty() ? new ArrayList<User>() : session.select("getByIds", ids);

这正是问题中提出的问题。

如果你真的想让 Mybatis 处理这个,那么生成的查询必须是有效的,因为必须执行(然后不跳过)到 return 空结果 快速 。这意味着忘记像 id = <!-- a value that will never exist in the table --> 这样的东西,因为它肯定会涉及(免费且无用的)完整扫描以搜索不存在的值。 那么:

    WHERE 
    <choose>
        <when test="ids==null || ids.isEmpty()">
            1 = 0 <!-- a test returning false, to adapt depending on you DB vendor -->
        </when>
        <otherwise>
            id IN <foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach>
        </otherwise>
    </choose>

确认的另一种选择是在查询执行前使用拦截器 "cancel" 查询,但这对于此处必须实现的目标来说绝对是过大的复杂性。

java代码函数

 List<ApiPriceChlogEntity> getApiAndDevPrice(@Param("apiKeys") List<String> currentApiKey, @Param("devKeys") List<String> currentDevKey, @Param("startDate") Date startDate);

映射器文件

<select id="getApiAndDevPrice"  resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_api_price_chlog tab1
<where>
    <if test="apiKeys.size() > 0">
      tab1.api_key IN
      <foreach collection="apiKeys" item="item" separator="," open="(" close=")" index="">
        #{item}
      </foreach>
    </if>
    <if test="devKeys.size() > 0">
      AND tab1.dev_key IN
      <foreach collection="devKeys" item="item" separator="," open="(" close=")" index="">
        #{item}
      </foreach>
    </if>

    <if test="startDate != null">
      AND tab1.change_date >= #{startDate}
    </if>
</where>

我已经测试过了,希望能帮到你。

使用 mybatis 拦截器,创建一个 nil PreparedStatement 对象并 return 它。

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
@Slf4j
public class MyBatisInterceptor implements Interceptor {
   @Override
   public Object intercept(Invocation invocation) throws Throwable {
      //...your code
      //return invocation.proceed();
      return new NullExecutor();
   }
}

MyBatis 想要获取一个 PreparedStatement 对象作为 return 值,"NullExecutor" 是:

public class NullExecutor implements PreparedStatement {}

没什么可做的,只是写一些东西,比如:

@Override
public void setDouble(int parameterIndex, double x) throws SQLException {
    //empty here
}
@Override
public boolean execute() throws SQLException {
    //it's ok,do noting.
    return false;
}
...etc

添加您的配置:

@Bean
public MyBatisInterceptor myBatisInterceptor() {
    return new MyBatisInterceptor();
}

你需要获取Interceptor中的"BoundSql",然后你可以获取sql和args,就google即可。

不知道能不能用,学习一下