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

wwulfric picture wwulfric · Mar 24, 2017 · Viewed 20.1k times · Source
select * from users where id in ()

The query is shown above.

<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>

If the Param ids is empty, Mybatis will throw BadSqlGrammarException, which generate a query like 'select * from users where id in'.

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

Answer

blackwizard picture blackwizard · Mar 24, 2017

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

To skip the query (not execute it), just don't call Mybatis. The calling code should check if ids is empty:

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

This is exactly what is asked in the question.

If you really want Mybatis to handle this, then produced query must be valid because must be executed (then not skipped) to return empty result quickly. that means forget something like id = <!-- a value that will never exist in the table --> because it could surely involve a (free and useless) full scan to search the unexisting value. Then:

    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>

Another option to confirm would consist in using interceptors to "cancel" the query before its execution, but this is definitely overkill complexity for what has to be achieved here.