使用Transactional进行Spring重试

时间:2018-04-05 17:53:44

标签: java spring spring-data spring-retry

Spring Retry是否可以保证使用Spring @Transactional注释?

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

致电代码 - >重试代理 - >交易代理 - >实际数据库代码

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

致电代码 - >交易代理 - >重试代理 - >实际数据库代码

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

在测试中,它似乎产生了第一个案例(重试,然后交易),但我无法判断这是保证行为还是幸运。

4 个答案:

答案 0 :(得分:2)

在这里找到答案: 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,这意味着只要您不是RetryableTransactional的组合,就可以安全覆盖其中任何一个注释的建议顺序。换句话说,您可以安全地使用此表单:

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

答案 1 :(得分:1)

如果你想独立测试它并确定它的行为,那么你可能有@Transactional @Service,然后是另一个使用事务一的服务,只是添加重试。

在这种情况下,无论您测试多少,您都依赖于未记录的行为(如何精确地进行注释处理)。这可能会在次要版本之间发生变化,基于创建独立Spring bean的顺序等等。简而言之,当您在同一方法上混合@Transactional和@Retry时,您会遇到问题。

编辑:有类似的已回答问题https://stackoverflow.com/a/45514794/1849837,代码为

@Retryable(StaleStateException.class)
@Transactional
public void doSomethingWithFoo(Long fooId){
    // read your entity again before changes!
    Foo foo = fooRepository.findOne(fooId);
    foo.setStatus(REJECTED)  // <- sample foo modification
} // commit on method end

在这种情况下似乎没问题,因为无论顺序是什么(重试然后交易,或交易或重试),可观察行为都是一样的。

答案 2 :(得分:1)

默认情况下,Spring Retry以相同的LOWEST_PRECEDENCE顺序构建建议-查看RetryConfiguration。 但是,有一种非常简单的方法可以覆盖此顺序:

@Configuration
public class MyRetryConfiguration extends RetryConfiguration {
   @Override
   public int getOrder() {
      return Ordered.HIGHEST_PRECEDENCE;
   }
}

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

答案 3 :(得分:1)

如果您使用的是Spring Boot,而您想使用@Retryable,则需要这样做:

  1. 将依赖项添加到pom:
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
  1. 在Spring Boot应用程序中启用重试:
@EnableRetry // <-- Add This
@SpringBootApplication
public class SomeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SomeApplication.class, args);
    }

}
  1. @Retryable注释方法:
@Retryable(value = CannotAcquireLockException.class,
        backoff = @Backoff(delay = 100, maxDelay = 300))
@Transactional(isolation = Isolation.SERIALIZABLE)
public boolean someMethod(String someArg, long otherArg) {
    ...
}

您可以使用@Retryable@Transactional注释相同的方法,它将按预期工作。