(轉)Spring事務不生效的原因大解讀


Spring團隊建議在具體的類(或類的方法)上使用 @Transactional 注解,而不要使用在類所要實現的任何接口上。在接口上使用 @Transactional 注解,只能當你設置了基於接口的代理時它才生效。因為注解是 不能繼承 的,這就意味着如果正在使用基於類的代理時,那么事務的設置將不能被基於類的代理所識別,而且對象也將不會被事務代理所包裝。


**原因一:**是否是數據庫引擎設置不對造成的。比如我們最常用的mysql,引擎MyISAM,是不支持事務操作的。需要改成InnoDB才能支持

**原因二:**入口的方法必須是public,否則事務不起作用(這一點由Spring的AOP特性決定的,理論上而言,不public也能切入,但spring可能是覺得private自己用的方法,應該自己控制,不應該用事務切進去吧)。另外private 方法, final 方法 和 static 方法不能添加事務,加了也不生效

**原因三:Spring的事務管理默認只對出現運行期異常(java.lang.RuntimeException及其子類)進行回滾(至於為什么spring要這么設計:因為spring認為Checked的異常屬於業務的,coder需要給出解決方案而不應該直接扔該框架)

原因四:@EnableTransactionManagement // 啟注解事務管理,等同於xml配置方式的 <tx:annotation-driven />
備注:本系列所有博文的討論都針對於springboot而不再對spring做說明。
@EnableTransactionManagement 在springboot1.4以后可以不寫。框架在初始化的時候已經默認給我們注入了兩個事務管理器的Bean(JDBC的DataSourceTransactionManager和JPA的JpaTransactionManager ),其實這就包含了我們最常用的Mybatis和Hibeanate了。當然如果不是AutoConfig的而是自己自定義的,請使用該注解開啟事務

**原因五:**請確認你的類是否被代理了(因為spring的事務實現原理為AOP,只有通過代理對象調用方法才能被攔截,事務才能生效)

**原因六:**請確保你的業務和事務入口在同一個線程里,否則事務也是不生效的,比如下面代碼事務不生效:

@Transactional
@Override
public void save(User user1, User user2) {
new Thread(() -> {
saveError(user1, user2);
System.out.println(1 / 0);
}).start();
}

**原因六:**也是我最想要去講的一個原因:service方法中調用本類中的另一個方法,事務沒有生效。這里我把當初保存的幾張對比圖貢獻給大家參考,一目了然:

圖一:事務不生效:.@Transactional的事務開啟 ,或者是基於接口的 或者是基於類的代理被創建。所以在同一個類中一個無事務的方法調用另一個有事務的方法,事務是不會起作用的(這就是業界老問題:類內部方法調用事務不生效的問題原因)

圖二:事務生效

圖三:事務生效

圖四:事務生效

圖五:事務生效(稍微解釋一下,這里雖然是方法內部調用,但是事務切入了addInfo方法,所以即使內部拋出異常,也是可以生效的)

圖六:事務不生效(准確的說這叫沒有事務)

圖七:事務生效。這里必須說幾句:這是我們解決方法內部調用事務不生效的最常用方法之一:內部維護一個注入自己的Bean,然后使用這個屬性來調用方法。其實還有一種方法,那就是利用Aop上下文來獲取代理對象(((TestService)AopContext.currentProxy()).create(); ),然后通過代理對象來調用。這里需要注意:Aop上下文spring默認是關閉的,需要手動開啟


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM