在使用 MyBatis 进行数据库操作时,<foreach> 标签是处理集合参数(如 ListArray)的利器,常用于:

  • IN 查询
  • 批量插入
  • 动态条件拼接

<foreach> 标签核心语法

<foreach collection="list" item="item" index="index" open="(" separator="," close=")">#{item}
</foreach>

属性详解

属性

说明

collection

传入的集合参数名(如 listarraymap.keySet()

item

当前遍历的元素别名

index

索引(List 为下标,Map 为 key)

open

循环开始前添加的字符串(如 (

separator

元素之间的分隔符(如 ,

close

循环结束后添加的字符串(如 )

实战场景 1:IN 查询优化

基础用法(小数据量)

<select id="selectByIds" resultType="User">SELECT * FROM user WHERE id IN <foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>

问题:大数据量 IN 查询

  • Oracle:IN 列表最多支持 1000 项
  • MySQL:受 max_allowed_packet 限制,过长 SQL 会报错
  • 性能差:数据库无法有效使用索引

解决方案:分批查询 + 合并结果

public List<User> selectByIds(List<Long> ids) {List<User> result = new ArrayList<>();int batchSize = 1000; // 每批 1000 个for (int i = 0; i < ids.size(); i += batchSize) {List<Long> subList = ids.subList(i, Math.min(i + batchSize, ids.size()));result.addAll(userMapper.selectByIds(subList));}return result;
}

实战场景 2:批量插入(Batch Insert)

方式一:单条插入(性能极差)

<insert id="insertUser">INSERT INTO user (name, email) VALUES (#{name}, #{email})
</insert>

循环调用 N 次 → N 次数据库交互

方式二:INSERT INTO ... VALUES (...), (...), (...)(推荐)

<insert id="batchInsert">INSERT INTO user (name, email) VALUES<foreach collection="users" item="user" separator=",">(#{user.name}, #{user.email})</foreach>
</insert>

优点

  • 一次 SQL,插入多条
  • 减少网络交互
  • 数据库可优化执行计划

实战场景 3:动态 INSERT 字段(可选字段插入)

有时我们只想插入非空字段,避免插入 NULL

<insert id="insertSelective">INSERT INTO user<trim prefix="(" suffix=")" suffixOverrides=","><if test="name != null">name,</if><if test="email != null">email,</if><if test="age != null">age,</if></trim>VALUES<trim prefix="(" suffix=")" suffixOverrides=","><if test="name != null">#{name},</if><if test="email != null">#{email},</if><if test="age != null">#{age},</if></trim>
</insert>

✅ 完美支持“只插入有值的字段”。