MyBatis 动态 SQL(一):if / where / choose
查询条件"有时有、有时没有"是常态:按名字搜、按年龄区间搜、还是两个条件一起搜?用字符串拼接会写成一场灾难。MyBatis 的动态 SQL 标签(if、where、choose…)让你在 XML 里按条件拼 SQL,干净又安全。
一个典型的"可选条件"需求
下面的查询要求:name 传了就按 name 过滤,age 传了就按 age 过滤,都不传就查全部。
if:条件成立才拼接
<select id="selectByCondition" resultType="user">
SELECT id, name, age FROM user
WHERE 1 = 1
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</select>
test 里是 OGNL 表达式,常见写法:判 null、判空串、比较大小(age > 18 注意 XML 转义)。先写 WHERE 1 = 1 是偷懒做法,能让后面的 AND 永远成立,但不优雅,更推荐下面的 where。
where:自动去掉多余 AND/OR
<select id="selectByCondition" resultType="user">
SELECT id, name, age FROM user
<where>
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
- 所有条件都不成立时,where 什么都不输出(不会有裸 WHERE)。
- 至少一个条件成立时,where 会自动去掉开头的 AND/OR,输出规范的 WHERE 子句。
- 原理:where 等价于 trim 的快捷写法(prefix="WHERE",prefixOverrides="AND |OR "),第 9 章讲 trim 时再展开。
choose / when / otherwise:多选一
if 是"各自独立可叠加",choose 则是"多个分支只命中第一个"(类似 Java 的 switch):
<select id="selectByKeyword" resultType="user">
SELECT id, name, age FROM user
<where>
<choose>
<when test="name != null and name != ''">
AND name = #{name}
</when>
<when test="age != null">
AND age = #{age}
</when>
<otherwise>
AND id > 0 <!-- 兜底分支,可省略 -->
</otherwise>
</choose>
</where>
</select>
执行逻辑:从上到下找第一个 test 为真的 when,执行它并跳过其余分支;一个都不满足时执行 otherwise。
其他常用标签:bind
bind 可以在 SQL 里先加工参数,比如模糊查询的 %关键字%:
<select id="selectByNameLike" resultType="user">
<bind name="likeName" value="'%' + name + '%'"/>
SELECT id, name, age FROM user
WHERE name LIKE #{likeName}
</select>
常见坑
- 判空要完整:字符串建议
!= null and != '',只判 null 时空串仍会拼出错误条件。 - test 是 OGNL:不支持的部分 Java 语法(如直接调用任意方法)在 test 里不可用,写法以官方文档为准。
- XML 里的小于号要转义:写
age < 18或把比较逻辑放进<、>转义中,直接写<会导致 XML 解析失败。
小结
动态条件三板斧:可叠加用 if(配合 where 去多余 AND/OR)、多选一用 choose/when/otherwise、加工参数用 bind。下一章继续 set/trim/foreach,解决更新与批量的动态拼接。