如何在调用saveAll()时获取所有数据库错误信息?
批量插入冲突捕获+事务回滚实现思路
方案1:预检查+批量插入(兼顾性能与冲突捕获)
- 先批量查询已存在的唯一键:把待插入的所有
CountryObject的iso2、iso3收集成键值对,用批量SQLSELECT iso2, iso3 FROM country WHERE (iso2, iso3) IN ((?,?), (?,?), ...)查询数据库中已存在的记录,拿到冲突的键值集合。 - 筛选冲突条目:对比待插入列表,把iso2/iso3在冲突集合里的对象挑出来,这就是预检查到的失败条目。
- 批量插入无冲突数据:用
saveAll()插入剩下的无冲突条目。 - 防并发竞态:整个流程放在事务里,查询时用
SELECT ... FOR UPDATE锁定已存在的记录,避免其他事务在检查和插入之间插入相同数据。如果插入过程中仍出现异常,直接触发事务回滚即可。
方案2:解析BatchUpdateException获取所有冲突
- 配置JDBC批量错误续跑:在数据源配置里开启
continueBatchOnError=true(比如Spring Boot里设置spring.datasource.hikari.data-source-properties.continueBatchOnError=true),让批量插入遇到错误时不立即终止,继续执行剩余条目。 - 自定义批量插入逻辑:不用JpaRepository的
saveAll(),改用JdbcTemplate或EntityManager执行原生批量插入SQL,这样能拿到完整的批量执行结果。 - 捕获并解析异常:捕获
BatchUpdateException,通过getUpdateCounts()方法获取每个条目执行状态,Statement.EXECUTE_FAILED对应的就是插入失败的条目。结合异常信息里的PostgreSQL唯一约束错误提示,确认是违反约束的失败项。 - 强制事务回滚:只要存在失败条目,调用
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()触发全量回滚,确保没有数据被保存。
方案3:利用PostgreSQL原生CTE捕获冲突
- 写原生SQL用CTE处理:
WITH input_data(iso2, iso3, name, ...) AS ( VALUES (?, ?, ?, ...), (?, ?, ?, ...) ), inserted AS ( INSERT INTO country (iso2, iso3, name, ...) SELECT iso2, iso3, name, ... FROM input_data ON CONFLICT (iso2, iso3) DO NOTHING RETURNING iso2, iso3 ) SELECT id.iso2, id.iso3 FROM input_data id LEFT JOIN inserted i ON id.iso2 = i.iso2 AND id.iso3 = i.iso3 WHERE i.iso2 IS NULL; - 执行SQL获取冲突列表:用
JdbcTemplate执行这个SQL,传入所有待插入参数,返回的结果就是所有违反唯一约束的条目。 - 触发回滚:如果返回的冲突列表不为空,直接标记事务回滚,确保没有数据写入数据库。
内容的提问来源于stack exchange,提问作者smb
相关产品推荐
相关产品推荐

