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

