spring事務@Transactional失效原因及解決辦法小結(jié)
spring事務@Transactional失效情況分析主要從以下幾個方面考慮:
1. mysql數(shù)據(jù)庫
默認情況下mysql數(shù)據(jù)庫使用的是Innodb存儲引擎(5.5版本之后),它是支持事務的,但是如果你的表的存儲引擎是MyISAM,MyISAM是不支持事務的。這樣就會出現(xiàn)“事務失效”的問題了。
解決方案:修改存儲引擎為Innodb。
2. 業(yè)務代碼
2.1 執(zhí)行事務的Bean交由Spring管理
我們要使用Spring的申明式事務,那么需要執(zhí)行事務的Bean是否已經(jīng)交由了Spring管理,在代碼中的體現(xiàn)就是類上是否有@Service、Component等一系列注解。
解決方案:將Bean交由Spring進行管理(添加@Service注解)
2.2 非public的方法進行事務管理
默認情況下你無法使用@Transactional
對一個非public的方法進行事務管理
解決方案:修改需要事務管理的方法為public
。
2.3 出現(xiàn)了自調(diào)用
多個方法都在同一個類中,其中第一個方法中調(diào)用了本類中的第二個和第三個方法,這就是自調(diào)用。
那么自調(diào)用為什么會導致事務失效呢?我們知道Spring中事務的實現(xiàn)是依賴于AOP的,當容器在創(chuàng)建Service這個Bean時,發(fā)現(xiàn)這個類中存在了被@Transactional標注的方法(修飾符為public)那么就需要為這個類創(chuàng)建一個代理對象并放入到容器中。由于方法實際上是由Service也就是目標類自己調(diào)用的,所以在方法的前后并不會執(zhí)行事務的相關(guān)操作。這也是自調(diào)用帶來問題的根本原因:自調(diào)用時,調(diào)用的是目標類中的方法而不是代理類中的方法。
解決方案:
- 自己注入自己,然后顯示的調(diào)用,
- 這種方案看起來不是很優(yōu)雅
- 利用
AopContext
,如下:
@Service public class DemoService { @Transactional public void save(A a, B b) { ((DemoService) AopContext.currentProxy()).saveB(b); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void saveB(B b){ dao.saveB(a); } }
使用上面這種解決方案需要注意的是,需要在配置類上新增一個配置
// exposeProxy=true代表將代理類放入到線程上下文中,默認是false @EnableAspectJAutoProxy(exposeProxy = true)
3. 事務回滾相關(guān)問題
3.1 該回滾的時候事務提交了
這種情況往往是程序員對Spring中事務的rollbackFor屬性不夠了解導致的。
Spring默認拋出了未檢查unchecked異常(繼承自 RuntimeException 的異常)或者 Error才回滾事務;其他異常不會觸發(fā)回滾事務,已經(jīng)執(zhí)行的SQL會提交掉。如果在事務中拋出其他類型的異常,但卻期望 Spring 能夠回滾事務,就需要指定rollbackFor屬性。
默認情況下,只有出現(xiàn)RuntimeException或者Error才會回滾
public boolean rollbackOn(Throwable ex) { ? ? return (ex instanceof RuntimeException || ex instanceof Error); }
所以,如果你想在出現(xiàn)了非RuntimeException或者Error時也回滾,請指定回滾時的異常,例如:
@Transactional(rollbackFor = Exception.class)
3.2 該提交的事務被回滾
對應的異常信息如下:
Transaction rolled back because it has been marked as rollback-only
總結(jié)起來,主要的原因就是因為內(nèi)部事務回滾時將整個大事務做了一個rollbackOnly的標記,所以即使我們在外部事務中catch了拋出的異常,整個事務仍然無法正常提交,并且如果你希望正常提交,Spring還會拋出一個異常。
3.3 解決方案
這個解決方案要依賴業(yè)務而定,你要明確你想要的結(jié)果是什么
內(nèi)部事務發(fā)生異常,外部事務catch異常后,內(nèi)部事務自行回滾,不影響外部事務
將內(nèi)部事務的傳播級別設(shè)置為nested/requires_new均可。在我們的例子中就是做如下修改:
// @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW) @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED) public void a() throws ClassNotFoundException{ // ...... throw new ClassNotFoundException(); }
雖然這兩者都能得到上面的結(jié)果,但是它們之間還是有不同的。當傳播級別為requires_new時,兩個事務完全沒有聯(lián)系,各自都有自己的事務管理機制(開啟事務、關(guān)閉事務、回滾事務)。但是傳播級別為nested時,實際上只存在一個事務,只是在調(diào)用a方法時設(shè)置了一個保存點,當a方法回滾時,實際上是回滾到保存點上,并且當外部事務提交時,內(nèi)部事務才會提交,外部事務如果回滾,內(nèi)部事務會跟著回滾。
內(nèi)部事務發(fā)生異常時,外部事務catch異常后,內(nèi)外兩個事務都回滾,但是方法不拋出異常
4. 讀寫分離跟事務結(jié)合使用時的問題
讀寫分離一般有兩種實現(xiàn)方式
- 配置多數(shù)據(jù)源
- 依賴中間件
如果是配置了多數(shù)據(jù)源的方式實現(xiàn)了讀寫分離,那么需要注意的是:如果開啟了一個讀寫事務,那么必須使用寫節(jié)點,如果是一個只讀事務,那么可以使用讀節(jié)點。
如果是依賴于中間件那么需要注意:需要根據(jù)中間件的事務規(guī)范使用事務。
到此這篇關(guān)于spring事務@Transactional失效原因及解決辦法小結(jié)的文章就介紹到這了,更多相關(guān)spring @Transactional失效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot yaml語法與數(shù)據(jù)讀取操作詳解
YAML 是 “YAML Ain’t Markup Language”(YAML 不是一種標記語言)的遞歸縮寫。在開發(fā)的這種語言時,YAML 的意思其實是:“Yet Another Markup Language”(仍是一種標記語言),本文給大家介紹的非常詳細,需要的朋友可以參考下2022-07-07java 引用類型的數(shù)據(jù)傳遞的是內(nèi)存地址實例
這篇文章主要介紹了java 引用類型的數(shù)據(jù)傳遞的是內(nèi)存地址實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10基于@RequestParam與@RequestBody使用對比
這篇文章主要介紹了@RequestParam與@RequestBody的使用對比,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10Java中基于注解的代碼生成工具MapStruct映射使用詳解
MapStruct?作為一個基于注解的代碼生成工具,為我們提供了一種更加優(yōu)雅、高效的解決方案,本文主要為大家介紹了它的具體使用,感興趣的可以了解下2025-02-02