有一个实体:
@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
更新时保存。
答案 0 :(得分: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);
}
首先回滚事务。
或者您可以关注文档并使用有状态重试https://docs.spring.io/spring-batch/trunk/reference/html/retry.html#statefulRetry