MyBatis 动态 SQL(二):set / trim / foreach
上一章的 if/where 解决"可选条件查询",这一章处理另外两个高频场景:动态更新(只更新非空字段)与批量操作(IN 条件、批量插入)。主角是 set、trim、foreach。
set:动态更新,自动去尾逗号
需求:只更新传入的非空字段。直接用 if 拼 SET 子句,最后一个条件后面会多一个逗号——set 标签自动修剪:
<update id="updateSelective" parameterType="user">
UPDATE user
<set>
<if test="name != null and name != ''">name = #{name},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
</update>
- 至少一个条件成立时,set 输出 SET 前缀并去掉末尾多余的逗号。
- 所有条件都不成立时会拼成
UPDATE user WHERE ...(缺 SET 子句、语法错误),调用方要保证至少传一个可更新字段。
trim:万能修剪器
trim 是 where/set 的"通用版",四个属性:
| 属性 | 作用 |
|---|---|
| prefix / suffix | 在拼接结果前/后追加内容 |
| prefixOverrides | 去掉结果开头的关键字(多个用 | 分隔) |
| suffixOverrides | 去掉结果结尾的关键字(如逗号) |
官方文档给出的等价关系:where ≈ trim prefix="WHERE" prefixOverrides="AND |OR ";set ≈ trim prefix="SET" suffixOverrides=","。自己写一个去掉开头 AND 的例子: |
<select id="selectByCondition" resultType="user">
SELECT id, name, age FROM user
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="name != null and name != ''">AND name = #{name}</if>
<if test="age != null">AND age = #{age}</if>
</trim>
</select>
foreach:IN 条件与批量插入
场景一:IN (...)——open/close 定首尾,separator 定分隔符:
<select id="selectByIds" resultType="user">
SELECT id, name, age FROM user
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
List<User> list = userMapper.selectByIds(Arrays.asList(1, 2, 3));
场景二:批量插入——item 是循环变量名,元素内用 #{item.属性} 引用:
<insert id="batchInsert">
INSERT INTO user(name, age) VALUES
<foreach collection="list" item="u" separator=",">
(#{u.name}, #{u.age})
</foreach>
</insert>
collection 到底填什么? 参数是集合且没加 @Param:List 填 list(或 collection)、数组填 array;加了 @Param("ids") 则填注解名;遍历 Map 时填其 key(item 为 value、index 为 key)。index 属性表示下标,多数场景可省略。建议一律用 @Param 显式命名,别依赖默认名。
组合使用示例
动态更新 + 条件删除经常叠着用:外层 where 控条件、内层 foreach 拼 id 列表,各标签互不冲突,可自由嵌套:
<delete id="deleteByCondition">
DELETE FROM user
<where>
<if test="name != null and name != ''">AND name = #{name}</if>
<if test="ids != null and ids.size() > 0">
AND id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
</where>
</delete>
小结
set 管动态更新(去尾逗号)、trim 是通用修剪器(where/set 的本质)、foreach 管 IN 与批量插入。配合 if/where,动态 SQL 已能覆盖绝大多数业务拼接需求。