JDBC原生批处理是底层可控的真正批量,foreach是SQL层合并的伪批量,ExecutorType.BATCH是MyBatis封装的JDBC批量;三者在网络往返、预编译开销和执行层级上本质不同。

Java 批量数据库操作中,JDBC 原生批处理、MyBatis 默认的 foreach 拼接方式、以及 MyBatis 的 ExecutorType.BATCH 模式,三者本质不同,不能混为一谈。关键区别不在“能不能批量”,而在于怎么批量、在哪一层批量、是否真正减少网络往返和预编译开销。
JDBC 原生批处理:最底层、最可控、性能天花板最高
直接使用 PreparedStatement.addBatch() + executeBatch(),绕过所有框架封装。优势明显:
- 完全掌控批大小、flush 时机、事务边界和异常回滚粒度
- 不依赖任何框架配置,兼容所有 JDBC 驱动(包括 Oracle、PostgreSQL)
- 配合 MySQL 的
rewriteBatchedStatements=true,可自动合并成多值 INSERT,性能提升显著 - 无 Mapper 映射、无 XML 解析、无 SqlSession 生命周期管理开销
缺点是代码冗长,需手动管理连接、事务、资源释放,易出错;不适合业务逻辑复杂、需复用 DAO 层的场景。
MyBatis 的 foreach 拼接:伪批量,本质仍是单条 SQL 执行
XML 中写 <foreach> 生成一条含多组 VALUES 的 INSERT 语句,例如:
立即学习“Java免费学习笔记(深入)”;
它不是真正的 JDBC Batch,而是SQL 层面的语法优化:
- 只发一次网络请求,但数据库仍按单条语句解析执行(MySQL 5.7+ 有优化,但非标准 Batch 行为)
- 受
max_allowed_packet限制,数据量大时容易报错 - 无法复用 PreparedStatement,每次执行都需重新解析 SQL(除非开启 prepareStatement 缓存)
- 不支持动态字段更新(如部分列 UPDATE),拼接逻辑易出错
适合中小批量(≤500 条)、结构固定、对事务一致性要求高的场景,比如初始化配置表。
MyBatis ExecutorType.BATCH:JDBC Batch 的封装,兼顾开发效率与性能
这是真正意义上的批量执行器,底层调用的就是 JDBC 的 addBatch() 和 executeBatch():
- 复用同一个 PreparedStatement,参数绑定高效,避免重复 SQL 解析
- 自动缓存多条 insert/update/delete,直到
flushStatements()或commit()才统一提交 - 必须配合 MySQL 连接参数
rewriteBatchedStatements=true,否则驱动默认拆成单条执行,性能无提升 - 需手动控制分批(建议 500–1000 条/批),防止 OOM 或触发 MySQL 包大小限制
- 不能用
useGeneratedKeys=true(主键回填会失效),需改用数据库自增或 UUID
它比原生 JDBC 简洁,比 foreach 更可靠,是业务系统中批量导入、同步类任务的首选方案。
一句话总结差异
JDBC 原生批处理是“裸金属”;MyBatis foreach 是“SQL 合并术”;ExecutorType.BATCH 是“带方向盘的 JDBC Batch 车”。选哪个,取决于你对可控性、开发成本和运行时性能的权衡。


















