日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring 事务方法与非事务方法相互调用 @Transactional 注解失效不回滚?

發布時間:2025/3/12 javascript 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring 事务方法与非事务方法相互调用 @Transactional 注解失效不回滚? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫這篇文章的初衷呢就是最近遇到了一個Spring事務的大坑。與其說是坑,還不如說是自己事務這塊兒太薄弱導致的(自嘲下)。

項目環境 Spring Boot

下面開始問題描述,發生的過程有點長,想直接看方案的直接跳過哦~!

最近在做項目中有個業務是每天定時更新xx的數據,某條記錄更新中數據出錯,不影響整體數據,只需記錄下來并回滾當條記錄所關聯的表數據;好啊!

這個簡單,接到任務后,樓主我三下五除二就寫完了,由于這個業務還是有些麻煩,我就在一個service里拆成了兩個方法去執行,一個方法(A) 是查詢數據與驗證組裝數據。

推薦后臺管理開源框架,基于 Spring Boot、Spring Security、JWT 后端框架,Vue & Element 前端框架的前后端分離的用戶權限管理系統,代碼易讀易懂、界面簡潔美觀。其核心技術采用 Spring、MyBatis、Shiro 沒有任何其它過度依賴包,下載即可運行使用。

另外一個方法(B)更新這條數據所對應的表(執行的時候是方法A中調用方法B);由于這個數據是循環更新,所以我想的是,一條數據更新失敗直接回滾此條數據就是,不會影響其他數據,其他的照常更新,所以我就在方法B上加了事務,方法A沒有加;以為很完美,自測一下正常。

ok通過,再測試一下報錯情況,是否回滾,一測沒回滾,懵圈兒?以為代碼寫錯了,改了幾處地方,再測了幾次,均沒回滾。這下是真難受了。

好啦,寫到這里相信各位看官心里肯定在嘲諷老弟了,spring的傳播機制都沒搞明白(難受)。

下面開始一步步分析解決問題:

首先我們來看下spring事務的傳播機制及原因分析;

PROPAGATION_REQUIRED – 支持當前事務,如果當前沒有事務,就新建一個事務。
這是最常見的選擇。
PROPAGATION_SUPPORTS – 支持當前事務,如果當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY – 支持當前事務,如果當前沒有事務,就拋出異常。
PROPAGATION_REQUIRES_NEW – 新建事務,如果當前存在事務,把當前事務掛起。
PROPAGATION_NOT_SUPPORTED – 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER – 以非事務方式執行,如果當前存在事務,則拋出異常。
PROPAGATION_NESTED – 如果當前存在事務,則在嵌套事務內執行。
如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。

spring默認的是PROPAGATION_REQUIRED機制,如果方法A標注了注解@Transactional** 是完全沒問題的,執行的時候傳播給方法B**,因為方法A開啟了事務,線程內的connection的屬性autoCommit=false,并且執行到方法B時。事務傳播依然是生效的,得到的還是方法A**的connection,autoCommit還是為false,所以事務生效;

反之,如果方法A沒有注解**@Transactional** 時是不受事務管理的,autoCommit=true,那么傳播給方法B的也為true,執行完自動提交,即使B標注了**@Transactional ;**

在一個Service內部,事務方法之間的嵌套調用,普通方法和事務方法之間的嵌套調用,都不會開啟新的事務。是因為spring采用動態代理機制來實現事務控制,而動態代理最終都是要調用原始對象的,而原始對象在去調用方法時,是不會再觸發代理了!

所以以上就是為什么我在沒有標注事務注解方法A里去調用標注有事務注解方法B而沒有事務滾回的原因。

看到這里,有的看官可能在想你在方法A上標個注解不就完了嗎?為什么非要標注在方法B上?

由于我這里是循環更新數據,調用一次方法B就更新一次數據,涉及到幾張表,需要執行幾條update sql,一條數據更新失敗不影響所有數據,所以說一條數據更新執行完畢后就提交一次事務,如果標注在方法A上,要所有的都執行完畢了才提交事務,這樣子是有問題滴。

下邊先上下代碼:

方法A:無事務控制

方法B:有事務控制

方法B處理失敗手動拋出異常觸發回滾:

方法A調用方法B:

從上圖可以看到,如果方法B中User更新出錯后需要回滾RedPacket數據,所以User更新失敗就拋出了繼承自RuntimeException的自定義異常,并且在調用方把這個異常catch到重新拋出,觸發事務回滾,但是并沒有執行;

解決方案

1、把方法B抽離到另外一個XXService中去,并且在這個Service中注入XXService,使用XXService調用方法B。

顯然,這種方式一點也不優雅,且要產生很多冗余文件,看起來很煩,實際開發中也幾乎沒人這么做吧?反正我不建議采用此方案;

2、通過在方法內部獲得當前類代理對象的方式,通過代理對象調用方法B

上面說了:動態代理最終都是要調用原始對象的,而原始對象在去調用方法時,是不會再觸發代理了!

所以我們就使用代理對象來調用,就會觸發事務;

綜上解決方案,我覺得第二種方式簡直方便到炸,那怎么獲取代理對象呢?這里提供兩種方式:

1、使用 ApplicationContext 上下文對象獲取該對象;

2、使用 AopContext.currentProxy() 獲取代理對象,但是需要配置exposeProxy=true

我這里使用的是第二種解決方案,具體操作如下:

springboot啟動類加上注解:@EnableAspectJAutoProxy(exposeProxy = true)

方法內部獲取代理對象調用方法

完了后再測試,數據順利回滾,至此,問題得到解決!

都是事務這塊兒基礎太差的錯啊~~希望各位遇到這種問題的兄弟些都好好的去研究研究spring這塊兒,好了不說了,我也該去深造了!

總結

以上是生活随笔為你收集整理的Spring 事务方法与非事务方法相互调用 @Transactional 注解失效不回滚?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。