1. 现象描述
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Transactional(rollbackFor = Exception.class)
public void methodA() {
try {
for (int i = 0; i < 10; i++) {
serviceB.methodB();
}
} catch (Exception e) {
// 异常处理
}
}
}
-------------------------------------------------------------------------------------------------------
@Service
public class ServiceB {
@Transactional(rollbackFor = Exception.class)
public void methodB() {
// 方法逻辑,可能会抛出异常
}
}
如示例
方法A
和 方法B
都有事务,方法A
一共执行了10次 方法B
,其中 方法B
可能抛出异常,所以在循环调用 方法B
时加上了 try catch
预想的结果: 失败的 方法B
回滚事务,成功的 方法B
提交事务
最终的结果: 方法A
执行的所有数据全部回滚
2. 问题分析
导致这个问题的重点是需要李姐 嵌套事务中的事务默认传播特性 ,方法A
执行的所有数据全部回滚,我们可以知道,是 方法A
上标识的 @Transactional 准备别事务管理器切面检测到了,才会导致 方法A
事务全部回滚。
可是报错明明在 方法B
,为什么会导致 方法A
的事务回滚呢?我们都知道,@Transactional 事务的默认传播机制是 Propagation.REQUIRED,即:若有事务,则复用,没有则创建一个。
所以 方法A
和 方法B
事务是同一个事务,在 方法B
执行的异常在catch住之前就被事务检测到了,所以 方法A
/方法B
的事务就会rollback。
3. 解决方案
- 去除
方法A
的事务 方法B
的事务传播机制改为:Propagation.REQUIRED_NEW- try catch 放到
方法B
内部
ps:
同一个类中调用被注解标识的方法,可能会导致注解的aop失效 比如说:@Transactional、@Async等
因为同一个类中的调用属于方法直接引用,而并不是调用这个类的代理对象,需要保证调用的时候是从容器里面获取的代理对象才能让注解生效