? ? ? 在使用Spring注解進行事務(wù)控制的時候惫企,我們都習(xí)慣性用@Transactional 注解進行處理曙咽,但是指值得注意的一點是真正出現(xiàn)異常的時候,你的事務(wù)真正回滾了嗎怖辆?以下就列舉幾種添加@Transactional但是事務(wù)回滾沒有生效的情景邪码。
一裕菠、除非特殊配置只有定義在 public 方法上的 @Transactional 才能生效
? ? ? ? 原因是,Spring 默認通過動態(tài)代理的方式實現(xiàn) AOP闭专,對目標方法進行增強奴潘,private 方法無法代理到,Spring 自然也無法動態(tài)增強事務(wù)處理邏輯喻圃。
二、必須通過代理過的類從外部調(diào)用目標方法才能生效
Spring 通過 AOP 技術(shù)對方法進行增強粪滤,要調(diào)用增強過的方法必然是調(diào)用代理后的對象斧拍。
(1)同一個類中通過this調(diào)用的場景
? ? ? 圖1中的代碼邏輯通過this調(diào)用,拋出RuntimeException 是不會回滾事務(wù)的杖小,具體原因是this 指針代表對象自己肆汹,而不是Spring注入的代理愚墓。而只有通過代理訪問的方法才有可能進行回滾。而代理和this 的區(qū)別如圖2所示:this指的是TestController這個類昂勉,而真正經(jīng)歷Spring注入的代理是有“CGLIB”字樣的testService浪册。只有經(jīng)過testService代理直接調(diào)用的方法在出現(xiàn)異常的時候才會進行回滾。
三岗照、不是所有的異常都會觸發(fā)回滾
? ? ? 默認情況下村象,出現(xiàn) RuntimeException(未檢查異常)或 Error 的時候,Spring 才會回滾事務(wù)攒至。此處需要解釋一下Java中的異常:
? ? ? Java中異常分為兩大類:checkedexception(檢查異常)和unchecked exception(未檢查異常)厚者。
? ? ? 未檢查異常也可以叫做RuntimeException(運行時異常),他們的主要區(qū)別:對于運行時異常迫吐,java編譯器不要求捕獲或者一定要繼續(xù)拋出,但是必須捕獲或者拋出檢查異常库菲。
? ? 常見的檢查異常:Exception,FileNotFoundException,IOException,SQLException。
? ? ? 常見的未檢查異常:? ? ? NullPointerException,ClassCastException,ArrayIndexsOutOfBoundsException,ArithmeticException(算術(shù)異常志膀,除0溢出)熙宇。
? ? (1)如果在代碼中對異常進行了try catch 操作使得RuntimeException(未檢查異常)或 Error 沒有拋出來,此時事務(wù)業(yè)務(wù)不會回滾溉浙,也可以正常執(zhí)行烫止。如果你希望自己捕獲異常進行處理的話,也沒關(guān)系放航,可以手動設(shè)置讓當前事務(wù)處于回滾狀態(tài)烈拒。具體代碼如圖3所示。
? (2)在注解中聲明广鳍,期望遇到所有的 Exception 都回滾事務(wù)(來突破默認不回滾受檢異常的限制)可以使用如下代碼對所有的異常進行回滾荆几。
@Transactional(rollbackFor = Exception.class)
四、多次數(shù)據(jù)庫操作回滾互不影響
在一個事務(wù)中有多個更新數(shù)據(jù)庫的操作赊时,但是又不想因為某一個數(shù)據(jù)庫操作的回滾影響到其他操作的完成吨铸。
為了描述方便我們把不需要回滾的稱為主流程,能夠回滾的是子流程祖秒。如果不做額外處理的話子流程如果回滾诞吱,即使子流程的代碼被try catch 住由于子流程已經(jīng)將該事務(wù)標注為回滾事務(wù),最后主流程也會跟著回滾竭缝。
解決這個問題的辦法讓子流程在獨立事務(wù)中運行房维,也就是為子流程注解加上 propagation = Propagation.REQUIRES_NEW 來設(shè)置 REQUIRES_NEW 方式的事務(wù)傳播策略,也就是執(zhí)行到這個方法時需要開啟新的事務(wù)抬纸,并掛起當前事務(wù)即可咙俩。
@Transactional(propagation = Propagation.REQUIRES_NEW)
總結(jié):
(1)我們務(wù)必確認調(diào)用 @Transactional 注解標記的方法是 public 的,并且是通過 Spring 注入的 Bean 進行調(diào)用的。
(2)因為異常處理不正確阿趁,導(dǎo)致事務(wù)雖然生效但出現(xiàn)異常時沒回滾膜蛔。Spring 默認只會對標記 @Transactional 注解的方法出現(xiàn)了 RuntimeException 和 Error 的時候回滾,如果我們的方法捕獲了異常脖阵,那么需要通過手動編碼處理事務(wù)回滾皂股。如果希望 Spring 針對其他異常也可以回滾,那么可以相應(yīng)配置 @Transactional 注解的 rollbackFor 和 noRollbackFor 屬性來覆蓋其默認設(shè)置命黔。
(3)如果方法涉及多次數(shù)據(jù)庫操作呜呐,并希望將它們作為獨立的事務(wù)進行提交或回滾,那么我們需要考慮進一步細化配置事務(wù)傳播方式纷铣,也就是 @Transactional 注解的 Propagation 屬性卵史。