環境配置
模塊 | 版本 |
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的內容