Spring Data:重试时回滚事务

10

这里有一个实体:

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

乐观方式实现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);
}

如评论所述,当StaleStateException发生时,似乎事务并没有回滚,因此每次重试都保存了B实例。

是否可能在重试时回滚事务?

期望的行为是只有在成功更新a时才保存b

1个回答

11
我认为这可能与@Retryable配置相关。
根据文档https://docs.spring.io/spring-batch/trunk/reference/html/retry.html#statelessRetry,无状态重试只是一个循环,它会不断调用相同的方法直到成功为止。
问题在于每次失败后,第一个被调用的拦截器是重试机制,并且它不会重新抛出异常,因此永远不会到达@Transactional拦截器。
所以每次重试都将遵循默认的事务传播方式,该方式会在上下文中重复使用相同的打开事务并在上下文中创建new B()
您可以通过调试来验证我的想法:如果在更新块之前进入第二个重试并发现A已经更新,则我应该是正确的。
您可以有两种方式解决:
要么分开两个块(首先进行嵌套事务重试)。
@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);
}

为了使事务首先回滚。

或者您可以遵循文档并使用有状态的重试https://docs.spring.io/spring-batch/trunk/reference/html/retry.html#statefulRetry


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接