Jakarta EE Batch任务失败时如何回滚数据库变更避免无效Delivery实体留存
解决方案
核心问题根因
- 你当前在
doOpen()阶段就独立提交事务持久化Delivery实体,此时后续文件写入、批处理逻辑还未执行,一旦后续环节失败,提前提交的Delivery数据自然无法回滚 - 数据库序列的设计天生不支持回滚:所有主流关系型数据库(Oracle/PostgreSQL/MySQL等)的序列为了保证高并发下无重复主键,取值后即使事务回滚也不会回收序号,完全不消耗序列值的需求无法通过原生序列实现
标准非侵入式修复方案
方案1:调整持久化时机+JTA事务绑定(推荐,无业务侵入)
- 移除
persistNewLieferung()方法内部的独立事务提交逻辑,不要在doOpen()阶段执行entityManager.persist()操作,仅在内存中初始化newDelivery实体的非主键字段 - 新增
JobListener,在作业执行成功的回调中再执行Delivery实体的持久化并提交事务:
@Named public class DeliveryJobListener implements JobListener { @PersistenceContext(unitName = MyPersistenceUnit.NAME) private EntityManager entityManager; @Inject private MyItemWriter myItemWriter; @Override public void beforeJob(JobExecution jobExecution) { // 不需要提前操作 } @Override public void afterJob(JobExecution jobExecution) { // 只有作业执行成功才持久化Delivery if (jobExecution.getExitStatus().equals(ExitStatus.COMPLETED)) { Delivery newDelivery = myItemWriter.getNewDelivery(); // 补全所有字段后持久化 entityManager.persist(newDelivery); // 若此处是容器管理事务则不需要手动提交,否则手动提交事务 } else { // 作业失败,删除已生成的不完整文件 File incompleteFile = new File(myItemWriter.getDeliveryFilePath()); if (incompleteFile.exists()) { incompleteFile.delete(); } } } }
- 调整
close()方法逻辑,增加执行成功状态判断,只有作业成功时才写入特殊记录:
@Override public void close() throws IOException { // 从批处理上下文中获取当前作业状态 JobContext jobContext = BatchRuntime.getJobOperator().getJobContext(jobExecutionId); if (ExitStatus.COMPLETED.equals(jobContext.getExitStatus())) { Delivery previousDelivery = getPreviousDelivery(); writeToFile(createRecord(newDelivery.getDeliveryNo().toString(), writtenRecords, previousDelivery.getDeliveryDate(), previousDelivery.getDeliveryNo().toString())); } fileWriter.close(); }
方案2:状态标记法(适合要求配送号连续的场景)
如果必须保证配送编号完全连续无缺口,放弃数据库序列主键,改为自行维护序号表:
- 新增序号维护表
DELIVERY_SEQ,仅存一个字段current_max_no,记录当前已成功使用的最大配送编号 Delivery实体去掉@GeneratedValue注解,主键自行赋值- 作业执行成功后,先对序号表加行锁读取最新编号+1作为本次配送号,再持久化
Delivery实体、更新序号表,两个操作在同一个事务中提交,保证原子性
方案3:失败反向清理(适合存量系统小改动场景)
如果不想调整现有持久化逻辑,新增批处理异常监听器,作业执行失败时:
- 手动删除
doOpen()阶段插入的未完成Delivery记录 - 删除不完整的配送文件
- 过滤查询时仅查关联文件存在、且写入完成的配送记录即可
内容的提问来源于stack exchange,提问作者Manu
相关产品推荐
相关产品推荐

