36. 从零开始学springboot-事务失效问题详解
前言
小编最近使用springboot事务时,发现了点问题
问题
ServiceA
@Transactional
public void addStudent() {
Student student = new Student();
student.setAge(20);
student.setName("学生");
studentMapper.insert(student);
int i = 1 / 0;
}
public void addStudent2() {
addStudent();
}
可以看出,我们在addStudent()方法上加了事务注解@Transactional,
所以,方法内抛出任意异常,都会回退,记录不会插入成功!
addStudent2()方法仅仅是内部调用了addStudent()方法,没有其它操作,效果应该和addStudent()一模一样.
我们在Controller直接调用addStudent()方法,会发现抛出了异常,记录没有被成功插入,而是被回退了.

但是,当我们调用addStudent2()时,发现虽然也抛出异常了,但是记录居然插入成功了!!
为什么会出现这种情况?
原因
SpringBoot 会扫描@Transactional的类和方法,并通过动态代理实现启动事务的代理方法。
在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调
用,都不会开启新的事务. 是因为Spring采用动态代理机制来实现事务控制,而动态
代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理
了!
一句话描述就是
同一个类中,非事务方法(addStudent2)调用事务方法(addStudent),事务是无法生效的.
解决方案
- 将带有@Transactional注解的方法移到另一个类中,发起类之间的方法调用。
ServiceB
@Transactional(propagation = Propagation.REQUIRED)
public void addStudent() {
Student student = new Student();
student.setAge(20);
student.setName("学生");
studentMapper.insert(student);
int i = 1 / 0;
}
ServiceA
public void addStudent2() {
ServiceB.addStudent();
}
- 在入口类添加注解方式暴漏代理对象,
@EnableAspectJAutoProxy(exposeProxy = true)

记得在pom.xml引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>

然后在Service中通过代理对象AopContext.currentProxy()去调用方法。
@Transactional(propagation = Propagation.REQUIRED)
public void addStudent() {
Student student = new Student();
student.setAge(20);
student.setName("学生");
studentMapper.insert(student);
int i = 1 / 0;
}
public void addStudent2() {
((TransactionService)AopContext.currentProxy()).addStudent();
}
以上两种方式均能使得事务生效.
说到这里,顺便看了下事务的传播机制
SpringBoot 事务传播机制
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(@Transactional 默认使用这种方式)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.