我有一个spring批处理工作,在happy path场景中按我想要的方式工作,但现在我将重点放在错误处理上。
我的目标是跳过一组已知错误,并在任何其他异常(如db错误或外部api错误)上使作业失败。稍后我将重新启动作业。为此,我创建了如下步骤配置
.faultTolerant()
.skip(SkippableException.class)
.skip(FlatFileParseException.class)
.skipLimit(Integer.MAX_VALUE)
.retryLimit(0)
在一个集成测试中,我已经证明,如果在我的读卡器/处理器/编写器中抛出可跳过的异常,那么作业将适当地跳过一个坏记录。
不过,在另一个测试中,我想证明一个不可预见的数据库错误将导致作业失败。为此,我创建了一个触发器,该触发器会导致对要插入的表的插入失败。
这似乎是可行的,在我的writer执行之后,在事务提交期间抛出异常,我得到以下日志消息:
2019-11-14 16:12:15.183 ERROR 88508 --- [ main] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.exception.GenericJDBCException: could not execute statement]
2019-11-14 16:12:15.184 INFO 88508 --- [ main] o.s.batch.core.step.tasklet.TaskletStep : Commit failed while step execution data was already updated. Reverting to old version.
这似乎也是预期的行为。问题是,这并不能阻止工作。该步骤退出到SimpleRetryExceptionHandler,它似乎认为异常不是致命的。它“重试”该块并将其标记为成功,然后继续作为成功完成。
如果我通过在自己的逻辑中导致db错误而强制在reader/processor/writer中抛出此异常,则作业成功。。失败。
我需要做些什么来处理作为事务提交的一部分发生的异常的重试/跳过逻辑吗?
更新:我可以确认我的跳过/重试设置是正确的,因为如果我在我的writer中插入entitymanager并调用flush(),作业将正确失败。但我绝对不想这么做。
更新2:再看一眼,框架提供的jpaitemwriter实现似乎在write()方法的末尾调用entitymanager.flush。。。所以我也可以这么做。
1条答案
按热度按时间j13ufse21#
在我自己的reader/processor/writer代码中没有抛出异常,这似乎是一个问题,因为在事务尝试提交之前,sql语句不会执行。
解决方法也可以在spring批处理jpaitemwriter中看到,就是将entitymanager注入到writer中并手动调用flush()。这将强制从我的代码中抛出任何sql异常,并且我的容错步骤按预期处理它们。