Spring Retry是否可以保证使用Spring @Transactional
注释?
具体来说,我试图使用@Retryable
进行乐观锁定。似乎它将依赖于创建的AOP代理的顺序。例如,如果调用如下所示:
致电代码 - >重试代理 - >交易代理 - >实际数据库代码
然后它会正常工作,但如果代理的结构如下:
致电代码 - >交易代理 - >重试代理 - >实际数据库代码
然后重试将不起作用,因为关闭事务的行为是抛出optmistic锁定异常的行为。
在测试中,它似乎产生了第一个案例(重试,然后交易),但我无法判断这是保证行为还是幸运。
答案 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
,这意味着只要您不是Retryable
与Transactional
的组合,就可以安全覆盖其中任何一个注释的建议顺序。换句话说,您可以安全地使用此表单:
@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
,则需要这样做:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
@EnableRetry // <-- Add This
@SpringBootApplication
public class SomeApplication {
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
}
@Retryable
注释方法:@Retryable(value = CannotAcquireLockException.class,
backoff = @Backoff(delay = 100, maxDelay = 300))
@Transactional(isolation = Isolation.SERIALIZABLE)
public boolean someMethod(String someArg, long otherArg) {
...
}
您可以使用@Retryable
和@Transactional
注释相同的方法,它将按预期工作。