使用Transactional进行Spring重试

Cob*_*117 10 java spring spring-data spring-retry

Spring Retry是否可以保证与Spring的@Transactional注释一起使用?

具体来说,我正在尝试使用@Retryable乐观锁定.似乎它将依赖于创建的AOP代理的顺序.例如,如果调用如下所示:

调用代码 - >重试代理 - >事务代理 - >实际数据库代码

然后它将正常工作,但如果代理结构如下:

调用代码 - >事务代理 - >重试代理 - >实际数据库代码

然后重试将不起作用,因为关闭事务的行为是抛出optmistic锁定异常的行为.

在测试中,它似乎生成了第一个案例(重试,然后交易),但我不知道这是保证行为还是幸运.

Cob*_*117 10

在这里找到答案:https : //docs.spring.io/spring/docs/5.0.6.BUILD-SNAPSHOT/spring-framework-reference/data-access.html#transaction-declarative-annotations 表 2 表示建议因为Transactional注释的顺序为Ordered.LOWEST_PRECEDENCE,这意味着只要您不覆盖这些注释中的任何一个的建议顺序,就可以安全地结合Retryable使用Transactional。换句话说,您可以安全地使用这种形式:

@Retryable(StaleStateException.class)
@Transactional
public void performDatabaseActions() {
    //Database updates here that may cause an optimistic locking failure 
    //when the transaction closes
}
Run Code Online (Sandbox Code Playgroud)

  • 但是,“@Retryable”注释的建议也具有“Ordered.LOWEST_PRECEDENCE”的顺序!看起来您的结论(关于使用安全性)是不正确的。如果“@Retryable”和“@Transactional”的两个建议具有相同的顺序,则生成的顺序是未定义的(即不可信)。因此,为了使其可靠,需要更改“@Retryable”建议的顺序。有多种方法可以做到这一点,但从 `spring-retry` 2.0.1(尚未发布)开始,`@EnableRetry` 注解有了新的 `order` 参数。 (2认同)

Rus*_*nko 6

At the moment (spring-retry version < 2.0.1), by default the order is undefined as both advices for @Retryable and @Transactional annotations have order = Ordered.LOWEST_PRECEDENCE.

It happens that RetryConfiguration advice implements IntroductionAdvisor interface. There is also method org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply that adds into list all found advisors, but it does it using 2 consecutive loops: at first it adds all advisors that implement IntroductionAdvisor interface and then all other avisors (this is why RetryConfiguration advisor always happen to be before BeanFactoryTransactionAttributeSourceAdvisor in this list). Then org.springframework.core.annotation.AnnotationAwareOrderComparator.sort method is called, but because both advisors have the same order, the method keeps the order of advisors in the list.

So, basically, the reason why @Retryable advisor is applied before @Transactional advisor at the moment is just "by accident", or "implementation detail". It could be changed any moment by changing implementation of AopUtils.findAdvisorsThatCanApply method or AnnotationAwareOrderComparator etc. So, it's better to not rely on this implementation detail in your code. :)

Starting from spring-retry version 2.0.1 (not released yet) there is new order attribute of @EnableRetry annotation that is equal to Ordered.LOWEST_PRECEDENCE - 1 by default to make sure @Retryable advice will always be ordered before @Transactional advice (when @Transactional advice order is default).

The order attribute could also be set to any other value, according to your needs, e.g. Ordered.LOWEST_PRECEDENCE - 4.

@EnableRetry(order = Ordered.LOWEST_PRECEDENCE - 4)
@Configuration
public class MyAppConfiguration {}
Run Code Online (Sandbox Code Playgroud)

With this configuration (and starting from spring-retry 2.0.1 - by default) the order of applying advices will be as this:

@Retryable
  @Transactional
    Your method body
  End of @Transactional
End of @Retryable
Run Code Online (Sandbox Code Playgroud)


oce*_*ize 5

默认情况下,Spring Retry 使用相同的 LOWEST_PRECEDENCE 顺序构建建议 - 查看 RetryConfiguration。然而,有一个非常简单的方法可以覆盖这个顺序:

@Configuration
public class MyRetryConfiguration extends RetryConfiguration {
   @Override
   public int getOrder() {
      return Ordered.HIGHEST_PRECEDENCE;
   }
}
Run Code Online (Sandbox Code Playgroud)

确保省略 @EnableRetry 注释以避免考虑默认的 RetryConfiguration。