Spring Data:重试时回滚事务

Ali*_*der 9 java hibernate optimistic-locking spring-data spring-boot

有一个实体:

@Entity
class A {
    ...
    @Version
    int version; 
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

如注释中所述,似乎事务在StaleStateException发生时不会回滚,因此B每次重试时都会保存实例.

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

所需的行为b仅在成功a更新时保存.

Zer*_*mus 8

我认为这可能与@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);
}
Run Code Online (Sandbox Code Playgroud)

这样事务就会先回滚.

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