addBatch + executeBatch 不一定快,因默认 autoCommit=true、未启用驱动批量协议(如 MySQL 需 rewriteBatchedStatements=true)、预编译语句未优化;须手动 setAutoCommit(false),配对连接参数,用纯 PreparedStatement 占位符,批大小 500–2000。
为什么 addBatch + executeBatch 不一定快
直接用 addbatch 塞几千条再 executebatch,结果比单条 executeupdate 还慢——这很常见。根本原因不是 jdbc 批处理本身低效,而是默认没关自动提交、没调优预编译语句、驱动没启用批量协议。
-
autoCommit = true时,每批执行完都隐式提交,彻底废掉批量意义;必须手动connection.setAutoCommit(false) - MySQL 驱动默认不走 server-side 批量(如
rewriteBatchedStatements=true),addBatch只是攒 SQL 字符串,最后还是发 N 条独立语句 - PostgreSQL 需要
reWriteBatchInserts=true(注意拼写)才能把多条INSERT合成一条带多个VALUES的语句 - Oracle 的
oracle.jdbc.useFetchSizeWithLongColumn等参数会影响大字段批量性能,但多数人根本没配
MySQL 下开启真正批量的三步实操
MySQL 是最典型“看着用了批量,其实没生效”的场景。关键不在 Java 代码,而在连接字符串和驱动行为。
- 连接 URL 必须加
?rewriteBatchedStatements=true,例如:jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true - 使用
PreparedStatement,且 SQL 中不能有动态表名或列名——只有参数占位符?才能被驱动重写 - 每批大小建议 500–2000 行:太小起不到合并效果,太大可能触发
max_allowed_packet报错,错误信息是Packets larger than max_allowed_packet are not allowed
示例片段:
String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
conn.setAutoCommit(false);
for (int i = 0; i
<h3>PostgreSQL 和 Oracle 的关键差异点</h3>
<p>PostgreSQL 和 Oracle 对批量的支持逻辑完全不同,照搬 MySQL 写法大概率失效。</p>
- PostgreSQL 驱动需加
?reWriteBatchInserts=true(注意是reWrite不是rewrite),且只对标准INSERT INTO ... VALUES有效;如果 SQL 含子查询或ON CONFLICT,批量会被降级为逐条执行 - Oracle 要想批量生效,必须用
PreparedStatement+addBatch,且不能混用executeUpdate;另外setFetchSize对批量插入无意义,别乱设 - Oracle 的
executeBatch返回的int[]数组里,-2 表示“操作成功但影响行数未知”,不是错误——很多人误判失败而重试
容易被忽略的异常处理与资源清理
批量出错时,executeBatch 抛的是 BatchUpdateException,它里面藏着每个子操作的更新计数和具体失败位置,但绝大多数人只 catch SQLException 就完事了。
- 必须捕获
BatchUpdateException,调用.getUpdateCounts()查看哪些位置失败(返回Statement.EXECUTE_FAILED的索引) - 出错后,连接仍处于事务中,不
rollback()会导致后续操作全卡在锁上;但也不能盲目commit(),得先确认已成功部分是否可接受 -
clearBatch()不是万能清道夫:如果某次addBatch传了 null 或类型不匹配的参数,会在executeBatch时才爆SQLException,此时 batch 内容已损坏,必须重建PreparedStatement
真正的麻烦在于:批量越快,出错时定位成本越高。别省那几行日志代码,至少记下当前 batch 起始 ID 和 SQL 片段。










