文章目錄
- 簡單說說spring事務失效的場景
- Spring 事務失效的7種場景
- 1.1、未啟用[spring事務管理](https://so.csdn.net/so/search?q=spring事務管理&spm=1001.2101.3001.7020)功能
- 1.2、方法不是public類型的
- 1.3、數據源未配置事務管理器
- 1.4、自身調用問題
- 1.5、異常類型錯誤
- 1.6、異常被吞了
- 1.7、業務和spring事務代碼必須在一個線程中
- 2、如何快速定位事務相關bug?
簡單說說spring事務失效的場景
Spring 事務失效的7種場景
- 未啟用spring事務管理功能
- 方法不是public類型的
- 數據源未配置事務管理器
- 自身調用問題
- 異常類型錯誤
- 異常被吞了
- 業務和spring事務代碼必須在一個線程中
1、數據庫不支持事務
2、沒有配置事務管理器
3、事務所在的方法沒有被public修飾
4、異常被catch,沒有拋出,事務會失效
5、異常類型錯誤,默認是runtimeException才會回滾的
? 解決方案:加上@Transactional(rollbackFor = Exception.class)注解;這樣Exception也會回滾
6、用final或者static關鍵字修飾的方法事務會失效
7、事務需要從外部調用,Spring 自調事務用會失效。即相同類里邊,A 方法沒有事務,B 方法有事務,A 方法調用 B 方法,則 B 方法的事務會失效,這點尤其要注意,因為代理模式只攔截通過代理傳入的外部方法調用,所以自調用事務是不生效的。
1.1、未啟用spring事務管理功能
@EnableTransactionManagement 注解用來啟用spring事務自動管理事務的功能,這個注解千萬不要忘記寫了。
1.2、方法不是public類型的
@Transaction 可以用在類上、接口上、public方法上,如果將@Trasaction用在了非public方法上,事務將無效。
1.3、數據源未配置事務管理器
spring是通過事務管理器了來管理事務的,一定不要忘記配置事務管理器了,要注意為每個數據源配置一個事務管理器:
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);
}
1.4、自身調用問題
spring是通過aop的方式,對需要spring管理事務的bean生成了代理對象,然后通過代理對象攔截了目標方法的執行,在方法前后添加了事務的功能,所以必須通過代理對象調用目標方法的時候,事務才會起效。
看下面代碼,大家思考一個問題:當外部直接調用m1的時候,m2方法的事務會生效么?
@Component
public class UserService {public void m1(){this.m2();}@Transactionalpublic void m2(){//執行db操作}
}
??顯然不會生效,因為m1中通過this的方式調用了m2方法,而this并不是代理對象,this.m2()不會被事務攔截器,所以事務是無效的,如果外部直接調用通過UserService這個bean來調用m2方法,事務是有效的,上面代碼可以做一下調整,如下,@1在UserService中注入了自己,此時m1中的m2事務是生效的.
@Component
public class UserService {@Autowired //@1private UserService userService;public void m1() {this.userService.m2();}@Transactionalpublic void m2() {//執行db操作}
}
重點:必須通過代理對象訪問方法,事務才會生效。
1.5、異常類型錯誤
spring事務回滾的機制:對業務方法進行try catch,當捕獲到有指定的異常時,spring自動對事務進行回滾,那么問題來了,哪些異常spring會回滾事務呢?
并不是任何異常情況下,spring都會回滾事務,默認情況下,RuntimeException和Error的情況下,spring事務才會回滾。
也可以自定義回滾的異常類型:
@Transactional(rollbackFor = {異常類型列表})
1.6、異常被吞了
當業務方法拋出異常,spring感知到異常的時候,才會做事務回滾的操作,若方法內部將異常給吞了,那么事務無法感知到異常了,事務就不會回滾了。
如下代碼,事務操作2發生了異常,但是被捕獲了,此時事務并不會被回滾
@Transactional
public void m1(){//事務操作1try{//事務操作2,內部拋出了異常}catch(Exception e){}
}
1.7、業務和spring事務代碼必須在一個線程中
?? spring事務實現中使用了ThreadLocal,ThreadLocal大家應該知道吧,可以實現同一個線程中數據共享,必須是同一個線程的時候,數據才可以共享,這就要求業務代碼必須和spring事務的源碼執行過程必須在一個線程中,才會受spring事務的控制,比如下面代碼,方法內部的子線程內部執行的事務操作將不受m1方法上spring事務的控制,這個大家一定要注意
@Transactional
public void m1() {new Thread() {//一系列事務操作}.start();
}
2、如何快速定位事務相關bug?
2種方式
方式1:看日志
如果你使用了logback或者log4j來輸出日志,可以修改一下日志級別為debug模式,可以看到事務的詳細執行日志,幫助你定位錯誤
方式2:調試代碼
如果你對源碼比較了解,那么你會知道被spring管理事務的業務方法,執行的時候都會被TransactionInterceptor攔截器攔截,會進入到它的invoke方法中,咱們可以在invoke方法中設置一些斷點,可以看到詳細的執行過程,排錯也就比較容易了。
整體上來說,還是需要你深入理解原理,原理了解了,寫代碼的時候本身就會避免很多坑。