java 使用事务的Spring重试

bfnvny8b  于 2023-03-11  发布在  Java
关注(0)|答案(5)|浏览(249)

Spring Retry是否保证可以使用Spring的@Transactional注解?
具体来说,我尝试使用@Retryable进行乐观锁,它似乎依赖于创建的AOP代理的顺序,例如,如果调用如下所示:
调用代码-〉重试代理-〉事务代理-〉实际DB代码
那么它将正常工作,但如果代理的结构如下:
调用代码-〉事务代理-〉重试代理-〉实际DB代码
那么重试就不会起作用,因为关闭事务的行为会引发乐观锁定异常。
在测试中,它似乎生成了第一种情况(重试,然后是事务),但我不知道这是一个有保证的行为还是只是运气好。

nkoocmlb

nkoocmlb1#

在这里找到了答案:https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html#tx-decl-explained表2指出Transactional注解的通知顺序为Ordered.LOWEST_PRECEDENCE,这意味着只要不覆盖RetryableTransactional注解的通知顺序,就可以安全地合并RetryableTransactional

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

bzzcjhmw2#

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

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

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

tzxcd3kk

tzxcd3kk3#

如果您想独立测试它并确定它的行为,那么您可以使用@Transactional @Service,然后使用另一个使用事务1并只添加重试的服务。
在这种情况下,无论你做了多少测试,你都依赖于未记录的行为(注解处理的顺序)。这可能会在小版本之间改变,基于独立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

在这种情况下,它似乎是好的,因为无论是什么顺序(先重试后事务,或事务或重试),可观察到的行为将是相同的。

j91ykkif

j91ykkif4#

如果您使用的是Sping Boot 并且希望使用@Retryable,则需要执行以下操作:
1.将依赖项添加到pom:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

1.在Sping 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注解同一个方法,它将按预期工作。

swvgeqrz

swvgeqrz5#

目前(spring-retry版本〈2.0.1),默认情况下,订单是未定义,因为@Retryable@Transactional注解的通知都有order = Ordered.LOWEST_PRECEDENCE
碰巧RetryConfiguration advice实现了IntroductionAdvisor接口。还有一个方法org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply,它将所有找到的顾问添加到列表中,但它使用2个连续的循环来完成此操作:首先,它添加实现IntroductionAdvisor接口的所有顾问,然后添加所有其他顾问(这就是为什么RetryConfiguration顾问总是碰巧在此列表中的BeanFactoryTransactionAttributeSourceAdvisor之前)。然后调用org.springframework.core.annotation.AnnotationAwareOrderComparator.sort方法,但是由于两个顾问具有相同的顺序,因此该方法保持顾问在列表中的顺序。
因此,基本上,@Retryable advisor在@Transactional advisor之前应用的原因只是“偶然”,或者“实现细节”。它可以通过改变AopUtils.findAdvisorsThatCanApply方法或AnnotationAwareOrderComparator等的实现而随时改变。因此,最好不要在代码中依赖此实现细节。:)
spring-retry版本2.0.1(尚未发布)开始,默认情况下,@EnableRetry注解的新order属性等于Ordered.LOWEST_PRECEDENCE - 1,以确保@Retryable通知始终在@Transactional通知之前排序(当@Transactional通知顺序为默认时)。
order属性也可以根据您的需要设置为任何其他值,例如Ordered.LOWEST_PRECEDENCE - 4

@EnableRetry(order = Ordered.LOWEST_PRECEDENCE - 4)
@Configuration
public class MyAppConfiguration {}

使用此配置(从spring-retry 2.0.1开始-默认),应用通知的顺序如下:

@Retryable
  @Transactional
    Your method body
  End of @Transactional
End of @Retryable

相关问题