Spring 使用事务重试
Spring Retry with Transactional
Spring 重试是否保证与 Spring 的 @Transactional
注释一起工作?
具体来说,我正在尝试使用 @Retryable
进行乐观锁定。它似乎取决于所创建的 AOP 代理的顺序。例如,如果调用如下所示:
调用代码 -> 重试代理 -> 事务代理 -> 实际数据库代码
那么它会正常工作,但如果代理的结构如下:
调用代码 -> 事务代理 -> 重试代理 -> 实际数据库代码
然后重试将不起作用,因为关闭事务的行为会引发乐观锁定异常。
在测试中,它似乎生成了第一个案例(重试,然后是交易),但我无法判断这是一个有保证的行为还是只是幸运。
如果您想独立测试它并确定它的行为方式,那么您可能有 @Transactional @Service,然后是另一个使用事务 1 并仅添加重试的服务。
在这种情况下,无论您测试多少,您都依赖于未记录的行为(注释处理的确切排序方式)。这可能会在次要版本之间发生变化,具体取决于创建独立 Spring bean 的顺序等等。简而言之,当您在同一方法上混合使用 @Transactional 和 @Retry 时,您会遇到问题。
编辑:有类似的已回答问题 代码
@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
在那种情况下似乎没问题,因为无论顺序是什么(重试然后交易,或交易或重试)可观察到的行为都是相同的。
在这里找到答案:
https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html#tx-decl-explained
Table 2表示对Transactional
注解的advice的顺序是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
}
默认情况下 Spring 重试使用相同的 LOWEST_PRECEDENCE 顺序构建建议 - 查看 RetryConfiguration。
但是,有一种非常简单的方法可以覆盖此顺序:
@Configuration
public class MyRetryConfiguration extends RetryConfiguration {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
确保省略 @EnableRetry 注释以避免默认 RetryConfiguration 被考虑在内。
如果您正在使用 Spring 启动并且您想要使用 @Retryable
,这就是您需要做的:
- 将依赖添加到 pom:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
- 在您的 Spring 引导应用程序中启用重试:
@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
注释相同的方法,它将按预期工作。
Spring 重试是否保证与 Spring 的 @Transactional
注释一起工作?
具体来说,我正在尝试使用 @Retryable
进行乐观锁定。它似乎取决于所创建的 AOP 代理的顺序。例如,如果调用如下所示:
调用代码 -> 重试代理 -> 事务代理 -> 实际数据库代码
那么它会正常工作,但如果代理的结构如下:
调用代码 -> 事务代理 -> 重试代理 -> 实际数据库代码
然后重试将不起作用,因为关闭事务的行为会引发乐观锁定异常。
在测试中,它似乎生成了第一个案例(重试,然后是交易),但我无法判断这是一个有保证的行为还是只是幸运。
如果您想独立测试它并确定它的行为方式,那么您可能有 @Transactional @Service,然后是另一个使用事务 1 并仅添加重试的服务。
在这种情况下,无论您测试多少,您都依赖于未记录的行为(注释处理的确切排序方式)。这可能会在次要版本之间发生变化,具体取决于创建独立 Spring bean 的顺序等等。简而言之,当您在同一方法上混合使用 @Transactional 和 @Retry 时,您会遇到问题。
编辑:有类似的已回答问题 代码
@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
在那种情况下似乎没问题,因为无论顺序是什么(重试然后交易,或交易或重试)可观察到的行为都是相同的。
在这里找到答案:
https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html#tx-decl-explained
Table 2表示对Transactional
注解的advice的顺序是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
}
默认情况下 Spring 重试使用相同的 LOWEST_PRECEDENCE 顺序构建建议 - 查看 RetryConfiguration。 但是,有一种非常简单的方法可以覆盖此顺序:
@Configuration
public class MyRetryConfiguration extends RetryConfiguration {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
确保省略 @EnableRetry 注释以避免默认 RetryConfiguration 被考虑在内。
如果您正在使用 Spring 启动并且您想要使用 @Retryable
,这就是您需要做的:
- 将依赖添加到 pom:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
- 在您的 Spring 引导应用程序中启用重试:
@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
注释相同的方法,它将按预期工作。