ExecutorType.BATCH 是 MyBatis 的批量执行器,通过复用 PreparedStatement 并调用 JDBC 的 addBatch/executeBatch 减少网络往返、事务开销和 SQL 解析损耗,但需同 SqlSession、同语句类型、避免混用查询才能生效。

ExecutorType.BATCH 是什么,为什么能加速插入
它不是“开个开关就变快”的魔法,而是让 MyBatis 把多条 INSERT 语句攒在一起,交给 JDBC 的 PreparedStatement.addBatch() 和 executeBatch() 批量执行。绕过了单条 SQL 的网络往返、事务开销和 JDBC 驱动反复解析的损耗。
但前提是:你得用同一个 SqlSession,且所有操作必须是同一种语句(比如全是 insertUser),否则 MyBatis 会在遇到不同语句时自动 flush 上一批 —— 这就是很多人测不出性能提升的根本原因。
怎么正确启用 Batch 模式
不能只改 ExecutorType,还要配对调整使用方式,否则等于白设:
- 创建
SqlSession时显式传入ExecutorType.BATCH:SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
- 别在循环里反复调用
session.commit()或session.close()—— 批处理必须攒够再交,提前 commit 会强制 flush 当前 batch - 确保 DAO 方法映射的 SQL 是静态的(没动态
<if>切换字段或表名),否则 MyBatis 无法复用同一PreparedStatement - 批量插入后,记得手动
session.commit(),否则数据不落库;session.rollback()也只回滚未 flush 的部分
Batch 模式下 insert 返回值总是 0 的原因
这是 JDBC 规范决定的:executeBatch() 默认返回的是每条语句影响行数的数组,而 MyBatis 的 insert() 方法签名只接收单个 int,所以统一返回 0。
如果你需要知道每条记录是否成功,有两条路:
- 改用
session.insert(String statement, Object parameter)+ 自己捕获BatchUpdateException,再通过getUpdateCounts()分析失败位置 - 放弃逐条反馈,改用“全量校验”:插入完查主键或唯一字段是否存在,或者用数据库自增 ID 做范围断言
- 注意:MySQL 在
rewriteBatchedStatements=true参数开启时才真正合并为多值 INSERT;没开这个,JDBC 还是走一条条 addBatch + executeBatch,性能提升有限
Batch 模式踩坑最多的地方
不是配置错,而是“混用”:
- 在同一个
SqlSession中穿插select查询 —— MyBatis 遇到查询会立刻 flush 当前 batch,然后执行 select,再新建 batch;结果就是“看着用了 BATCH,实际还是单条跑” - 批量插入中途抛了异常(比如唯一键冲突),默认不会停止,而是继续执行后续语句,最后才抛
BatchUpdateException;你得自己检查getUpdateCounts()里的EXECUTE_FAILED标记位 - Oracle 用户要注意:它的 batch 不支持混合 DML(比如 insert + update 同 session),也不支持 RETURNING 子句,一用就报
ORA-00933: SQL command not properly ended - PostgreSQL 用户要确认驱动版本 ≥ 42.2.0,老版本对 batch 的
getUpdateCounts()支持不完整,可能全返回 -2
Batch 的收益高度依赖数据库驱动实现和网络延迟,本地 H2 测不出明显差距,但跨机房写 MySQL 时,10 万条插入从 8 秒降到 1.2 秒很常见——前提是,你没在中间夹一句 session.selectOne(...)。


















