(15)动态SQL中的if,foreach和一些其他的常用标签

✅ 一、为什么需要动态 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 子句

自动:

  • 去掉开头的 ANDOR
  • 如果内部无有效条件,整个 <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 个,你就能应付绝大多数业务场景!

相关推荐
曲幽12 分钟前
别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了
python·docker·web·pot·translate·libretranslate·arogstranslate
用户556918817532 小时前
#从脚本到独立程序:Python + Playwright 批量抓取的完整踩坑记录
python·自动化运维
倔强的石头_3 小时前
KingbaseES 新版MySQL 兼容版体验:旧版迁移 + 功能实测
数据库
兵慌码乱16 小时前
基于 MediaPipe 与 PySide2 的手势交互音乐控制系统实现:轻量化视觉交互全流程解析
python·opencv·计算机视觉·人机交互·手势识别·mediapipe·pyside2
luckdewei19 小时前
FastAPI 资产管理系统实战:复杂 ORM 关联、Alembic 迁移与 N+1 查询优化
python
aqi001 天前
15天学会AI应用开发(八)使用向量数据库实现RAG功能
人工智能·python·大模型·ai编程·ai应用
Csvn1 天前
`functools.lru_cache` —— 一行代码搞定缓存加速
后端·python
zzzzzz3101 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
金銀銅鐵2 天前
[Python] 从《千字文》中随机挑选汉字
后端·python