当前位置: 首页 > 知识库问答 >
问题:

Spring批处理托管事务回滚时重试不起作用

崔棋
2023-03-14
  @Bean
  public Step myStep(StaxEventItemReader<Response> staxEventResponseReader,
      ItemWriter<Response> itemWriter,
      ItemProcessor<? super Response, ? extends Response> responseProcessor) {
    return stepBuilderFactory.get("myStep")
        .<Response, Response>chunk(1)
        .reader(staxEventResponseReader)
        .processor(responseProcessor)
        .writer(itemWriter)
        //.faultTolerant().retryLimit(3).retry(Exception.class)
        .build();
  }

该步骤的writer的逻辑非常简单:它尝试从数据库中读取一行,一旦找到该行,就更新该行。我能够通过在find方法之后设置断点来重现ObjectOptimisticLockingFailureException,手动为数据库中的行颠簸version列并提交它,然后恢复。

但是,在我的步骤中取消注释重试定义后,没有尝试重试。经过一些调试后,Spring重试逻辑似乎在块的事务中;但是由于ObjectOptimisticLockingFailureException不是由编写器中的代码引发的,而是由Spring的块事务提交逻辑引发的,因此根本没有尝试重试。

Chunk Transaction Begin
    Begin Retry loop in FaultTolerantChunkProcessor.write()
        Writer logic in my Step
    End Retry loop
Chunk Transaction Commit - Throws ObjectOptimisticLockingFailureException

当我试图在编写器中显式地抛出ObjectOptimisticLockingFailureException时,重试逻辑完全按照预期工作。我的问题是:

  1. 如果异常不是在步骤中从编写器代码中抛出的,而是在Spring Batch提交块事务时,如何使重试逻辑工作?
  2. 另一个奇怪的行为是,当我通过颠簸数据库中的version列手动导致ObjectOptimisticLockingFailureException时,步骤中注释了重试定义,该步骤的最终状态为FAILED,这是意料之中的。但是如果重试定义未注释,则步骤的最终状态为完成。为什么?

共有1个答案

朱天逸
2023-03-14
  1. 如果异常不是在步骤中从编写器代码中抛出的,而是在Spring Batch提交块事务时,如何使重试逻辑工作?

这里有一个开放的问题:https://github.com/spring-projects/spring-batch/issues/1826。解决办法是(尝试预测并)在编写器中提交时抛出任何可能发生的异常。这是您已经尝试过的,并且在您说时已经确认了它的工作原理。当我试图在编写器中显式地抛出ObjectOptimisticLockingFailureException时,重试逻辑完全按照预期工作。

这与前一个问题有关,但由另一个问题引起:https://github.com/spring-projects/spring-batch/issues/1189。也就是说,可以在调试会话期间使用version字段来了解工作原理,但我不建议更改代码中的version列。Spring Batch在其乐观锁定策略中严重依赖此列,并且不希望在用户代码中更改此列的值,否则可能会发生意外行为。

 类似资料:
  • 我面临一个挑战,需要从SQL Server数据库中读取“未处理”的数据,处理数据,然后有选择地更新DB2数据库中的两到六个表,然后将该数据标记为在SQL Server上的原始数据库中已处理。在任何时候,如果出现任何故障,我希望所有更新都回滚。如果我有10个未处理的项目,9个良好,但有一个失败,我仍然希望9个良好的项目完成,第10个恢复到原始状态,直到我们可以研究问题并进行更正。 总体架构是,一个输

  • 我有两个实体,即公司和用户。一个公司可以有很多用户。因此,当我保存一个公司时,它也会在一个事务中创建一个用户。如果用户的插入由于某种原因失败了,我想公司的插入应该回滚,这是目前不发生的。 因此,如果不知何故,在请求中传递的电子邮件已经存在,那么用户实体将不会被保存,所以在这种情况下,我需要公司应该回滚。你知道我怎么才能做到这一点吗?

  • public void A()抛出ApplicationException{ } 这是方法B(): } 显然,如果删除方法B()中的catch块,就不会出现这种行为。现在,我想知道是否有一种方法可以回滚我的事务,即使我捕捉到方法B()中的异常。谢谢!!!!

  • 我想了解Spring Batch是如何进行事务管理的。这不是一个技术问题,而是一个概念性的问题:Spring Batch使用什么方法?这种方法的后果是什么? 让我试着澄清一下这个问题。例如,在TaskletStep中,我看到步骤执行通常如下所示: 准备步骤元数据的几个JobRepository事务 每一块要处理的业务事务 更多JobRepository事务,用区块处理的结果更新步骤元数据 这似乎是