环境配置
模块 | 版本 |
mysql | 5.7.44 |
SpringBoot | 2.1.3.RELEASE |
Mybatis Plus | 3.2.0 |
mysql-connector | 8.0.28 |
因为现在这家公司我在测试的时候发现出现了事物失效的情况,虽然调用的方法链路上都有Translate注解,但是依旧会出现事物会滚异常的情况。所以我想研究一下,看看这个代码到底是什么原因导致的事物失效。给各位打个点,不是因为方法内部调用导致的,本身方法内调用同类方法已经独立启动一个AopProxy。
事物失效的场景
1. 非public修饰的方法
在Java中的访问权限有四种,private、default、protected、public。非public方法修饰的方法,就算加上Translate注解,事物也不会生效。失效的原因是 在AbstractFallbackTransactionAttributeSource
类的computeTransactionAttribute
方法中有个判断,如果目标方法不是public,则TransactionAttribute
返回null,即不支持事务。
/**
* 非public方法,Transactional注解失效,导致事物无效
*/
@Transactional
private void authTypeTranslateUnable(){
mapper.insert(new TranslateTableA("a"));
mapper.insert(new TranslateTableA("b"));
// 数据库中应该不存在这两条数据
throw new RuntimeException("抛出异常");
}
2. final修饰
如果事物方法被final修饰,那么事物就会无效。原因是final修饰的方法无法进行动态代理,导致Spring无法通过动态代理实现事物。
/**
* final 修饰,Transactional注解失效,导致事物无效
*/
@Transactional
public final void finalTypeTranslateUnable(){
mapper.insert(new TranslateTableA("a"));
mapper.insert(new TranslateTableA("b"));
// 数据库中应该不存在这两条数据
throw new RuntimeException("抛出异常");
}
3. 方法内部调用
当调用方法没有开启事物,而被调用方法开启事物的时候,会导致被调用方的事物失效。同样是动态代理的原因,动态代理已经加到日后更新中,各位期待一下。这里简单解释一下为什么会失效,是因为在调用方法(innerMethodTranslateUnable)中,Spring已经生成动态代理类,之后调用innerMethodTranslateUnableStep2的时候,是在代理类中直接调用方法,所以Spring的Aop不会生效,就导致事物没有开启。但是如果我们在innerMethodTranslateUnable方法上添加事物,那么事物就会生效。
/**
* 事物可以生效的情况
*/
@Transactional
public void innerMethodTranslateUnable(){
this.innerMethodTranslateUnableStep2();
}
public void innerMethodTranslateUnableStep2(){
mapper.insert(new TranslateTableA("innerMethodTranslateUnable-a"));
throw new RuntimeException("抛出异常");
}
4. 未被Sping管理的Bean
这个就没啥好解释的了,bean没有被Spring管理,那你用Spring的事物注解,那肯定不行的。
5. 多线程调用方法导致事物失效
因为每一条线程获取到的数据库链接可能不一样,因为事物是针对一条数据库连接的,同一个事物无法在多条连接中实现
/**
* 多线程调用导致事物失效
* 因为每一条线程获取到的数据库链接可能不一样,因为事物是针对一条数据库连接的,同一个事物无法在多条连接中实现
*/
@Transactional
public void multThreadTranslateUnable(){
mapper.insert(new TranslateTableA("multThreadTranslateUnable-a"));
new Thread(() -> {
threadComponent.saveInfo();
}).start();
new Thread(() -> {
threadComponent.saveInfo();
}).start();
new Thread(() -> {
threadComponent.saveInfo();
}).start();
new Thread(() -> {
threadComponent.saveInfo();
}).start();
new Thread(() -> {
threadComponent.saveInfo();
}).start();
throw new RuntimeException("抛出异常");
}
6. 异常不匹配或者异常被方法内部try cache
7. 方法设置了不支持事物
/**
* 使用不开启事物,执行sql
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void errorTranslatePropagationTranslateUnable(){
mapper.insert(new TranslateTableA("innerPrivateMethodTranslateUnable-3"));
}
8. mysql的数据引擎不支持事物,例如MyISAM
为啥业务代码中事物不生效呢?我明天补一下,今天我要搞一下socket的内容