✅ 一、为什么需要动态 SQL?
静态 SQL 无法应对:
- 条件搜索(用户可能只填部分字段)
- 批量插入/更新
- 复杂 WHERE / SET 逻辑
MyBatis 提供了强大的标签体系,在 XML 中写"类 Java 逻辑" ,但安全(预编译)且高效。
🔥 二、最常用动态 SQL 标签(按使用频率排序)
1. <if> ------ 条件判断(90% 场景都用它)
xml
<select id="searchUsers" resultType="User">
SELECT * FROM user
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
✅ 关键点:
test表达式支持 OGNL(类似 Java 表达式)- 必须配合
<where>:自动去掉第一个AND,避免语法错误
2. <foreach> ------ 循环(批量操作必备)
场景1:IN 查询
xml
<select id="selectByIds" resultType="User">
SELECT * FROM user
<where>
<foreach collection="ids" item="id" open="id IN (" separator="," close=")">
#{id}
</foreach>
</where>
</select>
调用:userMapper.selectByIds(Arrays.asList(1, 2, 3));
场景2:批量插入
xml
<insert id="batchInsert">
INSERT INTO user (name, email) VALUES
<foreach collection="users" item="user" separator=",">
(#{user.name}, #{user.email})
</foreach>
</insert>
✅ 属性说明:
collection:传入的集合(List / Array / Map 的 key)item:循环变量名open/close:包裹前后separator:分隔符
3. <where> ------ 智能处理 WHERE 子句
自动:
- 去掉开头的
AND或OR - 如果内部无有效条件,整个
<where>不生成
xml
<where>
<if test="name != null">AND name = #{name}</if>
<if test="age != null">AND age = #{age}</if>
</where>
→ 无条件时:SELECT * FROM user(正确!)
→ 有 name 时:SELECT * FROM user WHERE name = ?(自动去 AND)
❌ 不要手写
WHERE 1=1!这是反模式。
4. <set> ------ 智能 UPDATE
自动:
- 去掉末尾的逗号
- 如果无更新字段,不生成
SET
xml
<update id="updateUser">
UPDATE user
<set>
<if test="name != null">name = #{name},</if>
<if test="email != null">email = #{email},</if>
</set>
WHERE id = #{id}
</update>
5. <choose>, <when>, <otherwise> ------ 类似 Java 的 switch
xml
<select id="searchUsers" resultType="User">
SELECT * FROM user
<where>
<choose>
<when test="name != null">
name LIKE CONCAT('%', #{name}, '%')
</when>
<when test="email != null">
email = #{email}
</when>
<otherwise>
status = 'ACTIVE'
</otherwise>
</choose>
</where>
</select>
✅ 适用于 互斥条件(只满足一个)
6. <trim> ------ 万能裁剪(底层实现 <where> 和 <set> 的基础)
xml
<!-- 等价于 <where> -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="...">...</if>
</trim>
<!-- 等价于 <set> -->
<trim prefix="SET" suffixOverrides=",">
<if test="...">...</if>
</trim>
💡 一般不用直接写
<trim>,除非有特殊需求。
⚠️ 三、大厂避坑指南(Code Review 高频问题)
| 问题 | 正确做法 |
|---|---|
| SQL 注入 | 永远用 #{},不要用 ${} 拼接条件值(除非是表名/列名,且严格校验) |
| 空集合导致语法错误 | <foreach> 前加 <if test="list != null and !list.isEmpty()"> |
| 性能差的模糊查询 | LIKE CONCAT('%', #{name}, '%') → 考虑全文索引或 ES |
| 过度动态 SQL | 复杂逻辑拆成多个 Mapper 方法,比一个"万能方法"更清晰 |
✅ 四、总结:标签速查表
| 标签 | 用途 | 必记要点 |
|---|---|---|
<if> |
条件判断 | 配合 <where> / <set> |
<foreach> |
循环 | collection, item, separator |
<where> |
智能 WHERE | 自动处理 AND/OR |
<set> |
智能 SET | 自动去逗号 |
<choose> |
多选一 | like switch |
<trim> |
自定义裁剪 | 高级用法 |
💡 终极建议 :
80% 的动态 SQL 用<if> + <where> + <foreach>就够了 。先掌握这 3 个,你就能应付绝大多数业务场景!