最近在写 MyBatis 的 mapper.xml 时遇到一个坑,记录一下。

问题是这样的:有个查询条件是状态字段(Integer 类型),当状态为 0 时,这个条件居然没生效。

排查了半天,发现问题出在 <if> 标签的判空逻辑上。

问题复现

mapper.xml 中的写法:

<select id="selectList" resultType="UserDO">
    SELECT * FROM user
    <where>
        <if test="status != null and status != ''">
            AND status = #{status}
        </if>
    </where>
</select>

调用时传入 status = 0,结果这个条件被忽略了,SQL 中没有 AND status = 0

原因分析

MyBatis 使用 OGNL 表达式,在 OGNL 中:

0 == '' // 结果是 true

所以当 status = 0 时:

status != null and status != ''
// 等价于
true and (0 != '')
// 等价于
true and false
// 结果是 false

条件不成立,<if> 标签里的内容就被跳过了。

解决方案

方案一:只判断非空(推荐)

<if test="status != null">
    AND status = #{status}
</if>

Integer 类型的字段,只需要判断 != null 就行了,不需要判断空字符串。

方案二:增加数值判断

<if test="status != null and status != '' and status >= 0">
    AND status = #{status}
</if>

这样写也可以,但有点多余,不如方案一简洁。

方案三:统一用 1 作为默认值

有些团队会约定 Integer 类型字段不用 0,用 1 表示默认状态。

// 状态:1-启用,2-禁用
private Integer status;

这样就不会有 0 的问题了,但这是规避问题,不是解决问题。

其他注意事项

字符串字段的判断

字符串类型的字段,判空时需要同时判断 null 和空字符串:

<if test="name != null and name != ''">
    AND name LIKE CONCAT('%', #{name}, '%')
</if>

List 判断

判断集合是否为空:

<if test="ids != null and ids.size() > 0">
    AND id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</if>

总结

这个坑很经典,但很容易踩:

  • Integer 类型字段判空时,只判断 != null 即可
  • 不要加 and xxx != '' 的判断,否则 0 会被当成空值
  • 字符串类型才需要同时判断 null 和空字符串

虽然是个小问题,但排查起来挺费时间的,记录一下以后避免再踩\~

扩展阅读

除了这个坑,MyBatis 中还有一些常见问题:

1. 参数为空时全表查询

<!-- 错误写法 -->
<if test="name != null and name != ''">
    AND name = #{name}  <!-- 只有 name 为空时不查,否则全表查 -->
</if>

应该明确查询条件,避免参数为空时无条件查询全表。

2. ​<where> 标签的使用

<select id="selectList" resultType="UserDO">
    SELECT * FROM user
    <where>
        <if test="name != null and name != ''">
            AND name = #{name}
        </if>
        <if test="status != null">
            AND status = #{status}
        </if>
    </where>
</select>

<where> 标签会自动处理 AND 前缀,避免 SQL 语法错误。

3. ​$ 和 ​# 的区别

<!-- #{} 是预编译,安全 -->
AND name = #{name}

<!-- ${} 是字符串拼接,有 SQL 注入风险 -->
ORDER BY ${orderBy}

能用 #{} 就用 #{}${} 只在表名、字段名等不能预编译的场景使用。

这些细节在写 MyBatis 时都要注意,很容易踩坑\~

最后修改:2026 年 01 月 26 日
如果觉得我的文章对你有用,请随意赞赏