前段时间,朋友问了我一个问题,说有一个service类中,有一个A()方法和B()方法, A()方法没有添加事
务,B()方法添加了一个默认的事务,A()方法中调用B()方法,如果B()方法抛出异常,那么A()方法B()方法是否会回滚?
今天就回顾一下这个问题,看看到底如何?回顾这个问题之前,需要先了解下事务的传播行为,事务的传播
行为共7种如下:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务,最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起, 两个事务之间没有关系。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
Spring默认事务传播行为:PROPAGATION_REQUIRED
@Service
public class TUserServiceImpl implements TUserService {
@Autowired
private TUserMapper tUserMapper;
@Override
public void addTuser() {
TUser user = new TUser();
user.setName("司总");
user.setAge(23);
tUserMapper.addTuser(user);
updateTuser();
}
@Transactional
@Override
public void updateTuser() {
TUser user = new TUser();
user.setId(1);
user.setName("张三他王大爷");
user.setAge(66);
tUserMapper.updateTuser(user);
throw new RuntimeException();
}
}
如果直接调用updateTuser()方法,因为updateTuser()方法开启了事务,抛出异常事务会进行回滚。
但是直接调用addTuser()方法,updateTuser()方法中抛出异常,addTuser()方法与updateTuser()
方法并没有回滚?why?是不是一脸黑人问号?
其实这个原因如下:
spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean
动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调
用时,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法
是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所
以就不会启动transaction,我们看到的现象就是@Transactional注解无效。
@Service
class A{
@Transactinal
method b(){...}
method a(){ //标记1
b();
}
}
//Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
class proxy$A{
A objectA = new A();
method b(){ //标记2
startTransaction();
objectA.b();
}
method a(){ //标记3
objectA.a(); //由于a()没有注解,所以不会启动transaction,而是直接调用A的实例的a()方法
}
}
当我们调用A的bean的a()方法的时候,也是被proxy$A拦截,执行proxy$A.a()(标记3),然而,由以上代
码可知,这时候它调用的是objectA.a(),也就是由原来的bean来调用a()方法了,所以代码跑到了(标记
1)。由此可见,(标记2)并没有被执行到,所以startTransaction()方法也没有运行。
附带案例
@Transactional
@Override
public void addTuser() {
TUser user = new TUser();
user.setName("司总");
user.setAge(23);
tUserMapper.addTuser(user);
updateTuser();
}
@Override
public void updateTuser() {
TUser user = new TUser();
user.setId(1);
user.setName("张三他王大爷");
user.setAge(66);
tUserMapper.updateTuser(user);
throw new RuntimeException();
}
addTuser()方法存在事务,updateTuser()方法不存在事务,这种方式调用addTuser()方法抛出异常会进行
回滚操作。原因想必大家都很清楚了,如果不清楚的我再解释解释:
addTuser()方法存在事务注解,调用addTuser()方法其实是通过代理类来调用的,会在代理类方法中开启
startTransaction() 然后再调用被代理类的addTuser()方法,addTuser()方法又调用了updateTuser()方
法所以它们是在同一个事务中的,抛出异常肯定会进行回滚。看下面这个代码图(借鉴了上面案例,重新修改了下)
@Service
class A{
@Transactinal
method addTuser(){
updateTuser();
}
method updateTuser(){
}
}
Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
class proxy$A{
A objectA = new A();
method addTuser(){
startTransaction();
objectA.addTuser();
}
method updateTuser(){
objectA.updateTuser();
}
}