Spring 数据:重试时回滚事务

Spring Data: rollback transaction on retry

有一个实体:

@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 个实例。

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

期望的行为是 b 仅在成功 a 更新时保存。

我认为这可能与@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