在使用 MyBatis 进行数据库操作时,<foreach>
标签是处理集合参数(如 List
、Array
)的利器,常用于:
IN
查询- 批量插入
- 动态条件拼接
<foreach>
标签核心语法
<foreach collection="list" item="item" index="index" open="(" separator="," close=")">#{item}
</foreach>
属性详解
属性 | 说明 |
| 传入的集合参数名(如 |
| 当前遍历的元素别名 |
| 索引(List 为下标,Map 为 key) |
| 循环开始前添加的字符串(如 |
| 元素之间的分隔符(如 |
| 循环结束后添加的字符串(如 |
实战场景 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>
✅ 完美支持“只插入有值的字段”。