mybatis批量插入需显式标注@param("list"),否则foreach因找不到collection="list"报错;mysql用insert+values多值语法,oracle用select...from dual+union all;性能瓶颈源于jdbc批处理未启用、max_allowed_packet限制及事务过大。

批量插入时 foreach 报错:Parameter 'list' not found
这是最常见的一踩就倒的坑——foreach 标签找不到集合参数。MyBatis 默认只把方法参数里**直接传入的单个对象或值**作为根上下文,如果方法签名是 insertBatch(List<user> list)</user>,那必须显式告诉 MyBatis:我要遍历的是这个 list。
正确写法是加 @Param("list") 注解:
void insertBatch(@Param("list") List<User> users);
否则 XML 里写 <foreach collection="list" ...></foreach> 就会报错。不加 @Param 时,MyBatis 会尝试用 param1 当键名,但这种写法脆弱且难维护,别赌运气。
MySQL 批量插入用 INSERT INTO ... VALUES (),() 还是 REPLACE INTO?
取决于业务语义。普通批量插入用 INSERT INTO + foreach 拼多组 VALUES 是最常用、最可控的方式;REPLACE INTO 实际是“删再插”,会触发删除日志、影响自增 ID、还可能误删关联数据。
除非你明确需要覆盖逻辑,否则别碰 REPLACE INTO 或 INSERT IGNORE。它们在批量场景下行为更难预测,比如部分失败时事务边界模糊。
示例(安全拼接):
<insert id="insertBatch">
INSERT INTO user (name, age) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.name}, #{item.age})
</foreach>
</insert>
separator="," 很关键,漏了会语法错误;item 是当前遍历元素的别名,别写成 user 后又在表达式里用 #{u.name}——名字要对上。
Oracle 不支持多值 VALUES,怎么用 foreach 兼容?
Oracle 12c 以前不支持 INSERT INTO ... VALUES (),(),必须拆成多个 INSERT 或用 UNION ALL 模拟。这时候 foreach 不能只拼 VALUES,得整个 SQL 块重写。
推荐做法:用数据库方言隔离,或改用 SELECT ... FROM DUAL UNION ALL SELECT ... FROM DUAL 形式:
<insert id="insertBatchForOracle">
INSERT INTO user (name, age)
<foreach collection="list" item="item" separator="UNION ALL">
SELECT #{item.name}, #{item.age} FROM DUAL
</foreach>
</insert>
注意:UNION ALL 前不能有逗号,separator 必须设为 "UNION ALL";FROM DUAL 不可省,否则 Oracle 解析失败。
更稳妥的做法是:不同数据库用不同 <sql></sql> 片段 + <if test="databaseId=='oracle'"></if> 判断,避免一个 SQL 强撑所有库。
批量插入性能卡在 1000 条就变慢?检查这三点
不是 foreach 本身慢,而是底层 JDBC 和数据库限制在起作用:
- MySQL 默认
max_allowed_packet通常为 4MB,拼太长 SQL 直接被截断或报错Packets larger than max_allowed_packet are not allowed - MyBatis 默认不开启批处理(
ExecutorType.BATCH),每次insertBatch()调用仍是逐条提交,没走 JDBC 的addBatch()/executeBatch() - 事务太大导致 undo log 膨胀,尤其在高并发写入时锁等待加剧
解决办法:控制单次批量大小(500~1000 条较稳)、Spring 中用 @Transactional 包裹、SqlSession 创建时指定 ExecutorType.BATCH。别指望一次塞 10000 条进 foreach 就能飞起来——分页切块才是真实世界里的常态。










