亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術文章
文章詳情頁

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

瀏覽:5日期:2023-07-14 18:27:58
1 前言

很多需要使用事務的場景,都只是在方法上直接添加個@Transactional注解

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

但是,你以為這真的夠了嗎?

事務如果未達到完美效果,在開發和測試階段都難以被發現,因為你難以考慮到太多意外場景。但當業務數據量發展,就可能導致大量數據不一致的問題,就會造成前人栽樹后人踩坑,需要大量人力排查解決問題和修復數據。

2 如何確認Spring事務生效了?

使用@Transactional一鍵開啟聲明式事務, 這就真的事務生效了?過于信任框架總有“意外驚喜”。來看如下案例

領域層 實體

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

領域服務

createUserError1調用private方法

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

createUserPrivate,被@Transactional注解。當傳入的用戶名包含test則拋異常,讓用戶的創建操作失敗

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

getUserCount

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

用戶接口層

調用UserService#createUserError1

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

測試結果即便用戶名不合法,用戶也能創建成功。刷新瀏覽器,多次發現有十幾個的非法用戶注冊。 @Transactional生效原則 public方法

除非特殊配置(比如使用AspectJ靜態織入實現AOP),@Transactional必須定義在public方法才生效。

因為Spring的AOP,private方法無法被代理到,自然也無法動態增強事務處理邏輯。

那簡單,把createUserPrivate方法改為public不就行了。但發現事務依舊未生效

必須通過代理過的類從外部調用目標方法

要調用增強過的方法必然是調用代理后的對象。嘗試修改UserService,注入一個self,然后再通過self實例調用標記有 @Transactional 注解的createUserPublic方法。設置斷點可以看到,self是由Spring通過CGLIB方式增強過的類:

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

CGLIB通過繼承實現代理類,private方法在子類不可見,所以無法進行事務增強。而this指針代表調用對象本身,Spring不可能注入this,所以通過this訪問方法必然不是代理。把this改為self,這時即可驗證事務生效:非法的用戶注冊操作可回滾。

雖然在UserDomainService內部注入自己調用自己的createUserPublic可正確實現事務,但這不符常規。更合理的實現方式是,讓Controller直接調用之前定義的UserService的createUserPublic方法。

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

this/self/Controller調用UserDomainService

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

this自調用

無法走到Spring代理類

后兩種

調用的Spring注入的UserService,通過代理調用才有機會對createUserPublic方法進行動態增強。

推薦開發時打開Debug日志以了解Spring事務實現的細節。比如JPA數據庫訪問,開啟Debug日志:logging.level.org.springframework.orm.jpa=DEBUG

開啟日志后再比較下在UserService中this調用、Controller中通過注入的UserService Bean調用createUserPublic的區別。

很明顯,this調用因沒走代理,事務沒有在createUserPublic生效,只在Repository的save生效:

// 在UserService中通過this調用public的createUserPublic[23:04:30.748] [http-nio-45678-exec-5] [DEBUG] [o.s.orm.jpa.JpaTransactionManager:370 ] - Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT[DEBUG] [o.s.orm.jpa.JpaTransactionManager :370 ] - Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT//在Controller中通過注入的UserService Bean調用createUserPublic[10:10:47.750] [http-nio-45678-exec-6] [DEBUG] [o.s.orm.jpa.JpaTransactionManager :370 ] - Creating new transaction with name [org.geekbang.time.commonmistakes.transaction.demo1.UserService.createUserPublic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

這種實現在Controller里處理異常顯得繁瑣,還不如直接把createUserWrong2加@Transactional注解,然后在Controller中直接調用該方法。這既能從外部(Controller中)調用UserService方法,方法又是public的能夠被動態代理AOP增強。

小結

務必確認調用被@Transactional注解標記的方法被public修飾,并且是通過Spring注入的Bean進行調用。

但有時因沒有正確處理異常,導致事務即便生效也不一定能回滾。

2 事務生效不代表能正確回滾

AOP實現事務:使用try/catch包裹@Transactional注解的方法:

當方法出現異常并滿足一定條件,在catch里可設置事務回滾 沒有異常則直接提交事務 一定條件

只有異常傳播出了被@Transactional注解的方法,事務才能回滾。

Spring的 TransactionAspectSupport#invokeWithinTransaction 就是在處理事務。觀察源碼得知,只有捕獲到異常后才能進行后續事務處理:

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

默認情況下,出現RuntimeException(非受檢異常)或Error,Spring才會回滾事務。

Spring的DefaultTransactionAttribute:

受檢異常一般是業務異常或類似另一種方法的返回值,出現這種異常可能業務還能完成,所以不會主動回滾 而Error或RuntimeException代表非預期結果,應該回滾

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

事務無法正常回滾的各種慘案 異常無法傳播出方法

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

受檢異常

注冊的同時會有一次文件讀,若讀文件失敗,希望用戶注冊的DB操作回滾。因讀文件拋的是受檢異常,createUserError2傳播出去的也是受檢異常

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

以上方法雖然避開了事務不生效的坑,但因異常處理不當,導致異常時依舊不回滾事務。

修復回滾失敗bug 1 手動設置讓當前事務處回滾態

若希望自己捕獲異常并處理,可手動設置讓當前事務處回滾態

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

查看日志,事務確定回滾。

Transactional code has requested rollback:手動請求回滾。關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

2 注解中聲明,期望所有Exception都回滾事務 突破默認不回滾受檢異常的限制

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

查看日志,提示回滾:

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

該案例有DB操作、IO操作,在IO操作問題時期望DB事務也回滾,以確保邏輯一致性。 小結

由于異常處理不正確,導致雖然事務生效,但出現異常時沒回滾。Spring默認只對被@Transactional注解的方法出現RuntimeException和Error時回滾,所以若方法捕獲了異常,就需要通過手寫代碼處理事務回滾。若希望Spring針對其他異常也可回滾,可相應配置@Transactional注解的rollbackFor和noRollbackFor屬性覆蓋Spring的默認配置。

有些業務可能包含多次DB操作,不一定希望將兩次操作作為一個事務,這時就需仔細考慮事務傳播的配置。

3 事務傳播配置是否符合業務邏輯

案例

用戶注冊:會插入一個主用戶到用戶表,還會注冊一個關聯的子用戶。期望將子用戶注冊的DB操作作為一個獨立事務,即使失敗也不影響注冊主用戶的流程。

UserService:創建主、子用戶

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

SubUserService:使子用戶注冊失敗。期望子用戶注冊作為一個事務單獨回滾而不影響注冊主用戶

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

啟動調用后查看日志:事務回滾了

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

不對呀!因為運行時異常逃出被@Transactional注解的createUserWrong,Spring當然會回滾事務。若期望主方法不回滾,應捕獲子方法所拋的異常。

修正方案

把subUserService#createSubUserWithExceptionError包上catch,這樣外層主方法createUserError2就不會出現異常

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

啟動后查看日志注意到:

對createUserError2開啟異常處理 子方法因出現運行時異常,標記當前事務為回滾 主方法捕獲異常并打印create sub user error 主方法提交事務

但Controller出現一個UnexpectedRollbackException,異常描述提示最終該事務回滾了且為靜默回滾:因createUserError2本身并無異常,只不過提交后發現子方法已把當前事務設為回滾,無法完成提交。

明明無異常發生,但事務也不一定可提交因為主方法注冊主用戶的邏輯和子方法注冊子用戶的邏輯為同一事務,子邏輯標記了事務需回滾,主邏輯自然也無法提交。那么修復方式就明確了,獨立子邏輯的事務,即修正SubUserService注冊子用戶方法,為注解添加propagation = Propagation.REQUIRES_NEW設置REQUIRES_NEW事務傳播策略。即執行到該方法時開啟新事務,并掛起當前事務。創建一個新事務,若存在則暫停當前事務。類似同名的EJB事務屬性。注:實際事務暫停不會對所有事務管理器外的開箱。 這特別適于org.springframework.transaction.jta.JtaTransactionManager ,這就需要javax.transaction.TransactionManager被提供給它(這是服務器特定的標準Java EE)

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

主方法無變化,依舊需捕獲異常,防止異常外泄導致主事務回滾,重命名為createUserRight:

關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)

修正后再查看日志

Creating new transaction with name createUserRight

對createUserRight開啟主方法事務createMainUser finish創建主用戶完成Suspending current transaction, creating new transaction with name createSubUserWithExceptionRight主事務掛起,開啟新事務,即對createSubUserWithExceptionRight創建子用戶的邏輯Initiating transaction rollback子方法事務回滾Resuming suspended transaction after completion of inner transaction子方法事務完成,繼續主方法之前掛起的事務create sub user error:invalid status主方法捕獲到了子方法的異常Committing JPA transaction on EntityManager主方法的事務提交了,隨后我們在Controller里沒看到靜默回滾異常

小結

若方法涉及多次DB操作,并希望將它們作為獨立事務進行提交或回滾,即需考慮細化配置事務傳播方式,即配置@Transactional注解的Propagation屬性。

4 總結

若要針對private方法啟用事務,動態代理方式的AOP不可行,需要使用靜態織入方式的AOP,也就是在編譯期間織入事務增強代碼,可以配置Spring框架使用AspectJ來實現AOP。

以上就是關于Spring的@Transaction導致數據庫回滾全部生效問題(又刪庫跑路)的詳細內容,更多關于Spring @Transaction數據庫回滾的資料請關注好吧啦網其它相關文章!

標簽: Spring
相關文章:
主站蜘蛛池模板: 真实偷清晰对白在线视频 | 日本人一级毛片免费完整视频 | 欧美日韩在线播一区二区三区 | 国产日产欧产美一二三区 | 亚洲美女激情 | 制服丝袜第一页在线观看 | 91不卡视频 | 一区二区三区四区在线视频 | 久久综合九色综合欧洲色 | 亚洲成人在线播放视频 | 51激情精品视频在线观看 | 国产精品亚洲片夜色在线 | 亚洲一区二区三区在线观看蜜桃 | 在线观看成年人视频 | 在线观看日韩一区 | 亚洲精品色综合色在线观看 | 在线看黄网 | 国产在线观看免费不卡 | 欧美日韩色视频在线观看 | 九九视频免费观看 | 国产午夜精品理论片久久影视 | 西川结衣在线精品视频 | 午夜一级毛片看看 | 视频在线观看免费播放www | 欧美日韩亚洲一区二区精品 | 亚洲午夜久久久久影院 | 中国一级毛片欧美一级毛片 | 欧美高清一级毛片免费视 | www.亚洲第一 | 欧美黑人性大免费高清视频 | 亚洲国产高清精品线久久 | 国产综合网站 | 亚洲高清美女一区二区三区 | 国产成+人+综合+亚洲 欧美 | 免费国产成人午夜在线观看 | 毛片色| 啪视频 | 欧美一区二区精品系列在线观看 | 国产精品福利在线观看 | 亚洲精品亚洲人成在线播放 | 中文字幕国产在线 |