batchupdateexception 表明批处理中至少一条 sql 失败,需检查驱动是否启用部分成功(如 mysql 加 continuebatchonerror=true 或 rewritebatchedstatements=true),并解析 getupdatecounts() 判断具体失败项。

遇到 BatchUpdateException,说明 JDBC 批处理执行过程中至少有一条 SQL 失败了,但不是所有语句都一定失败——JDBC 会尝试执行完全部语句(取决于驱动和配置),然后统一抛出异常,并附带各语句的更新计数(包括 Statement.EXECUTE_FAILED 标记)。
检查是否启用了批量失败继续执行(关键第一步)
默认情况下,MySQL 的 Connector/J(8.0+)在批处理中遇到错误时会中断整个批次,并把前面成功的语句也回滚(除非手动 commit)。而 PostgreSQL、Oracle 等行为可能不同。你需要确认驱动是否支持“部分成功”,以及是否启用:
- MySQL:加参数
&continueBatchOnError=true(旧版)或&allowMultiQueries=true(不推荐);新版更推荐用rewriteBatchedStatements=true(底层重写为 INSERT ... VALUES (...),(...),...,提升性能且天然支持原子性) - PostgreSQL:默认支持部分失败,
BatchUpdateException.getUpdateCounts()会返回混合结果(正数、0 或Statement.EXECUTE_FAILED) - HikariCP / Druid 等连接池一般不影响该行为,但需确保底层驱动版本兼容
捕获并解析 BatchUpdateException 的真实失败项
不要只打印异常堆栈,要主动读取 getUpdateCounts() 数组判断哪几条失败:
try {
stmt.executeBatch();
} catch (BatchUpdateException e) {
int[] counts = e.getUpdateCounts();
for (int i = 0; i
<h3>根据业务决定重试策略或降级处理</h3>
<p>不能一概吞掉异常或全量回滚,需结合场景选择:</p>
-
允许部分失败(如日志记录、消息上报类操作):遍历
updateCounts,跳过失败项,单独记录错误 SQL 和参数,继续后续流程 - 强一致性要求(如账户扣款、库存预占):应整体回滚(若在事务中),并抛出业务异常,由上层决定重试或告警
- 自动修复重试:对可识别错误(如乐观锁失败、主键冲突),提取失败数据,查最新状态后修正再单条重试(避免再次批量)
预防优于补救:提前校验 + 合理分批
很多 BatchUpdateException 其实可以避免:
- 插入前校验主键/唯一索引是否已存在(尤其多线程并发写入)
- 控制每批 size(如 50~500 条),太大会增加单点失败影响面,太小则失去批量意义
- 使用
PreparedStatement而非Statement,防止 SQL 注入同时提升预编译效率 - 对 MySQL,开启
rewriteBatchedStatements=true后,即使某条数据有问题,整个 INSERT 语句仍可能成功(因为被合并成一条),此时不会抛BatchUpdateException,而是返回实际插入行数 —— 这种方式更可控
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











