简体   繁体   English

Spring Data:重试时回滚事务

[英]Spring Data: rollback transaction on retry

There is an entity: 有一个实体:

@Entity
class A {
    ...
    @Version
    int version; 
}

A instances update implemented in optimistic manner: A实例更新的乐观的方式来实现:

@Transactional(rollbackFor = {StaleStateException.class})
@Retryable(value = {StaleStateException.class})
public void updateA() {
    A a = findA();
    B b = new B();
    // Update "a" somehow
    a.update();
    // "b" is saved on each retry!
    save(b);
}

As stated in comments, seems that transaction is not rollbacked when StaleStateException occurs, so B instance is saved on each retry. 如注释中所述,似乎发生StaleStateException时事务不会回滚,因此每次重试时都会保存B实例。

Is it possible to rollback transaction on retry? 是否可以在重试时回滚事务?

The desired behaviour is that b is saved only on successfull a update. 期望的行为是b仅在全成保存a更新。

I think it may be something related to the @Retryable configuration. 我认为它可能与@Retryable配置有关。

As the doc says https://docs.spring.io/spring-batch/trunk/reference/html/retry.html#statelessRetry a stateless retryable is nothing more than a cycle that keeps calling the same method until it succedes. 正如文档所述https://docs.spring.io/spring-batch/trunk/reference/html/retry.html#statelessRetry一样 ,无状态可重试只不过是一个循环,它一直调用相同的方法直到它成功。

The problem is that everytime it fails the first interceptor called is the retryable that will not rethrow the exception, so it does never reach the @Transactional one. 问题是,每次失败时,第一个被调用的拦截器都是可重试的,不会重新抛出异常,因此它永远不会到达@Transactional

So what happens is that every retry will follow the default transaction propagation which will reuse the same opened transaction with a new B() in the context. 所以会发生的是,每次重试都将遵循默认的事务传播,这将在上下文中重用相同的已打开事务和new B()

You can check if i'm on the right lead by debugging: if you enter a second retry and find that A is already updated before the update block then i should be right. 您可以通过调试检查我是否在正确的线索上:如果您输入第二次重试并发现A在更新块之前已经更新,那么我应该是正确的。

You can fix in 2 ways: 您可以通过两种方式修复:

Either divide the two block (retry first with nested transaction) 将两个块分开(首先使用嵌套事务重试)

@Retryable(value = {StaleStateException.class})
public void retryableUpdate() {
   updateA();
}

@Transactional(rollbackFor = {StaleStateException.class})
public void updateA() {
    A a = findA();
    B b = new B();
    // Update "a" somehow
    a.update();
    // "b" is saved on each retry!
    save(b);
}

So that the transaction is rolled back first. 这样事务就会先回滚。

Or you can follow the docs and use a stateful retry https://docs.spring.io/spring-batch/trunk/reference/html/retry.html#statefulRetry 或者您可以关注文档并使用有状态重试https://docs.spring.io/spring-batch/trunk/reference/html/retry.html#statefulRetry

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM