事务失效原因分析
一、自调用(最常见)
情境①:
methodA(无事务注解)调用methodB(有事务注解/无论事务propagation是如何定义的),会导致methodB的事务失效
代码参考:
@Service
public class ServiceA{
@Autowired
public XxMapper xxMapper;
@Override
public void methodA {
// dosomething
methodB();
}
@Override
@Transaction
public void methodB {
xxMapper.insert();
}
}
原因分析:
事务分为编程式事务和声明式事务,此例中采用的是声明式事务,即通过注解的形式使用事务。
声明式事务基于Spring AOP技术,Spring AOP的实现是靠(jdk实现接口/cglib继承类)动态代理的方式。
所以如果一个类的methodA调用本类中另一个方法methodB,实际上是这个类对自己的调用,并没有通过Spring容器去请求代理类,因此Spring AOP不起作用
换言之,在目前常规的web开发中,我们在Controller层调用Service层,这个调用不是通过在Controller层中new对象的形式去做的,而是通过Spring容器托管,由容器去帮我们调用。而在Serivce层中,如果methodA调本类中的methodB,默认是使用this.methodB(),这是不经过Spring容器的,所以Spring无法干涉。
情境②:
methodA(有事务注解,且propagation = Propagation.SUPPORTS | Propagation.NOT_SUPPORTS )调用methodB(有事务注解/无论事务propagation是如何定义的),会导致methodB的事务失效
代码参考:
@Service
public class ServiceA{
@Autowired
public XxMapper xxMapper;
@Override
@Transaction(propagation = Propagation.SUPPORTS)
// @Transaction(propagation = Propagation.NOT_SUPPORTS)
public void methodA {
// dosomething
methodB();
}
@Override
@Transaction
public void methodB {
xxMapper.insert();
}
}
原因分析:
此处分析情境②事务失效前先补充说明事务的传播特性
分类 | propagation | 描述 |
---|---|---|
事务一定存在 | Propagation.REQUIRED_NEW | 如果当前存在事务,则加入当前事务,如果当前不存在事务,则新建一个事务。 |
同上 | Propagation.REQUIRED | 如果当前不存在事务,则新建一个事务,如果存在事务,则将当前事务挂起,新建一个事务,两个事务独立。 |
同上 | Propagation.NESTED | 如果当前不存在事务,则新建一个事务,如果存在事务,则将当前事务挂起,新建一个事务,两个事务有依存关系。 |
报异常 | Propagation.NEVER | 如果当前(调用方)存在事务,则抛出异常 |
同上 | Propagation.MANDATORY | 如果当前(调用方)不存在事务,则抛出异常 |
中庸 | Propagation.SUPPORT | 如果当前存在事务,则加入当前事务,如果当前不存在事务,则以非事务的方式执行。 |
同上 | Propagation.NOT_SUPPORT | 如果当前存在事务,则将当前事务挂起,被调用方是非事务方式执行 |
根据表中的结论,不难分析。不论methodB的事务传播特性如何,它的事务是失效的,相当于没有在方法上加事务注解,所以只要methodA的事务传播特性不是REQUIRED_NEW/REQUIRED/NESTED,它的事务也不会生效。
情境③:
methodA(有事务注解,且propagation = Propagation.MANDATORY)调用methodB(有事务注解/无论事务propagation是如何定义的),会报异常
代码参考:
@Service
public class ServiceA{
@Autowired
public XxMapper xxMapper;
@Override
@Transaction(propagation = Propagation.MANDATORY)
// @Transaction(propagation = Propagation.NOT_SUPPORTS)
public void methodA {
// dosomething
methodB();
}
@Override
@Transaction
public void methodB {
xxMapper.insert();
}
}
原因分析:
Propagation.MANDATORY,要求当前方法中要有事务,但是却没有,所以会报
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
二、try{}catch{}异常
@Transaction注解中的属性rollback定义了出现什么异常时,事务回滚。
但是如果没有将异常抛出,做了try/catch处理,则Spring AOP无法感知,此时事务会失效
三、rollback的异常类型与真实异常不匹配
@Transaction注解默认的rollback属性为RuntimeException.class,如果业务流程中定义的异常类型不是RuntimeException或者其子类,则事务也不会生效
四、数据库Engine不支持事务
如果使用mysql,而恰巧又使用MyISAM作为数据库Engine。则无论是编程式事务还是声明式事务都不会生效。因此,一定要确保数据库Engine支持事务。
结语
这四种事务失效的场景是我从入门至今,真实体验过的场景。今天,恰巧听到项目组同事在讨论这个东西,就做一个总结回顾。可能上述有误,或者不够准确,因此我会不断回顾自己输出的内容,并在评论区对相应错误进行指出更正!