Spring transaction事務 roll back各種回滾


 Spring的AOP事務管理默認是針對unchecked exception回滾。

也就是默認對RuntimeException()異常極其子類進行事務回滾。

Exception作為基類,下面還分checked exception和unchecked exception。如果客戶端可以通過其他的方法恢復異常,那么這種異

常就是checked exception;如果客戶端對出現的這種異常無能為力,那么這種異常就是Unchecked exception;簡單來說,繼承於

RuntimeException的都是unchecked exception。

Error:
1.總是不可控制的(unchecked)
2.經常用來用於表示系統錯誤或低層資源的錯誤
3.如何可能的話,應該在系統級被捕捉

Exception:
1.可以是可被控制(checked) 或不可控制的(unchecked)
2.表示一個由程序員導致的錯誤
3.應該在應用程序級被處理

Java 中定義了兩類異常:
1) Checked exception: 這類異常都是Exception的子類。異常的向上拋出機制進行處理,假如子類可能產生A異常,那么在父類中

也必須throws A異常。可能導致的問題:代碼效率低,耦合度過高。
2) Unchecked exception: 這類異常都是RuntimeException的子類,雖然RuntimeException同樣也是Exception的子類,但是它們是

非凡的,它們不能通過client code來試圖解決,所以稱為Unchecked exception 。

解決辦法:

1.在針對事務的類中拋出RuntimeException異常,而不是拋出Exception。

2.在txAdive中增加rollback-for,里面寫自己的exception,例如自己寫的exception為

com.cn.untils.exception.***Exception

<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    <tx:method name="*" rollback-for="com.cn.untils.exception.***Exception"/>
  </tx:attributes>
</tx:advice>

或者

定義不會滾的異常

<tx:advice id="txAdvice">
   <tx:attributes>
      <tx:method name="update*" no-rollback-for="IOException"/>
      <tx:method name="*"/>
   </tx:attributes>
</tx:advice>

spring事務回滾.默認情況,unchecked異常,即運行時異常runntimeException回滾事務;checked異常,即Exception可try{}捕獲的不會回滾.當然也可配置spring參數讓其回滾.

 

 

 

試驗方法:

         寫一個單元測試,調用一個service層方法(發生對數據庫進行寫操作的方法--insert、update、delete)即可.

 

 

試驗過程:

         定義一個service方法如下:

         public SMSTiming createSMSTiming(SMSTiming smsTiming){

                   SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

                   return s;

         }

 

         定義二個異常(先默認配置TestException為Spring事務回滾異常):

            publicclass MyTestException extends Exception

            publicclass TestException extends Exception

 

         注意看下:每次這個方法的不同處(拋出的異常不同)。

 

測試1:

public SMSTiming createSMSTiming(SMSTiming smsTiming){

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       int i = 4/0; //人為產生異常(實際這里拋出了ArithmeticException運行異常)

       return s;

    }

測試1結果:會事務回滾----數據庫中未插入新數據。

 

 

測試2:

        public SMSTiming createSMSTiming(SMSTiming smsTiming) throws Exception{//受檢異常(非運行異常)必須拋出

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人為產生異常

       }catch(Exception e){

           thrownew Exception ("");//拋出Exception異常

       }

       return s;

    }

測試2結果:不會事務回滾----數據庫中插入新數據。

 

        

測試3:

            public SMSTiming createSMSTiming(SMSTiming smsTiming) throws RuntimeException{//運行異常(非受檢異常)可以不拋出

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人為產生異常

       }catch(Exception e){

           thrownewRuntimeException("");//拋出RuntimeException異常

       }

       return s;

    }

測試3結果:會事務回滾----數據庫中未插入新數據

 

測試4:

        public SMSTiming createSMSTiming(SMSTiming smsTiming) throws TestException{//受檢異常(非運行異常)必須拋出

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人為產生異常

       }catch(Exception e){

           thrownewTestException("");//拋出TestException異常

       }

       return s;

    }

測試4結果:會事務回滾----數據庫中未插入新數據。

 

測試5:

    public SMSTiming createSMSTiming(SMSTiming smsTiming) throws MyTestException{//受檢異常(非運行異常)必須拋出

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人為產生異常

       }catch(Exception e){

           thrownewMyTestException("");//拋出MyTestException異常

       }

       return s;

    }

 測試5結果:不會事務回滾----數據庫中插入新數據。

 

測試6:

    public SMSTiming createSMSTiming(SMSTiming smsTiming) throws MyTestException{//受檢異常(非運行異常)必須拋出 (注意:此時spring指定配置此異常回滾)

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人為產生異常

       }catch(Exception e){

           thrownewMyTestException("");//拋出MyTestException異常

       }

       return s;

    }

 測試6結果:會事務回滾----數據庫中未插入新數據。

 

 

試驗總結:

測試1、測試3、測試4、測試6會進行事務回滾;測試2、測試5不會進行事務回滾。

 

為什么會這樣?因為是異常的類型(受檢異常、運行時異常)不同或使用了Spring的rollback-for配置。

 

測試1和測試3是因為拋出了運行時異常,會事務回滾。

 

測試4和測試5、測試6分別拋出受檢異常TestException、MyTestException,那為什么測試4和測試6會事務回滾呢?

因為是我們在Spring事務配置中指定了此異常(指定rollback-for)。

 

最近遇到了事務不回滾的情況,我還考慮說JPA的事務有bug? 我想多了.......  
  為了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志。但是這邊情況來了,當這個方法異常時候 日志是打印了,但是加的事務卻沒有回滾。

  例:  
   類似這樣的方法不會回滾 (一個方法出錯,另一個方法不會回滾) :  

[html]  view plain  copy
 
  1. if(userSave){          
  2.     try {         
  3.         userDao.save(user);          
  4.         userCapabilityQuotaDao.save(capabilityQuota);         
  5.      } catch (Exception e) {          
  6.         logger.info("能力開通接口,開戶異常,異常信息:"+e);         
  7.      }         
  8.  }  


下面的方法回滾(一個方法出錯,另一個方法會回滾):

 

 

[html]  view plain  copy
 
  1. if(userSave){         
  2.      try {          
  3.         userDao.save(user);          
  4.         userCapabilityQuotaDao.save(capabilityQuota);         
  5.        } catch (Exception e) {         
  6.         logger.info("能力開通接口,開戶異常,異常信息:"+e);          
  7.         throw new RuntimeException();         
  8.      }          
  9. }  

或者:

[html]  view plain  copy
 
  1. if(userSave){          
  2.     try {          
  3.         userDao.save(user);          
  4.         userCapabilityQuotaDao.save(capabilityQuota);          
  5.     } catch (Exception e) {          
  6.         logger.info("能力開通接口,開戶異常,異常信息:"+e);          
  7.         TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();         
  8.     }         
  9.  }  

 

為什么不會滾呢??是對Spring的事務機制就不明白。!! 
   默認spring事務只在發生未被捕獲的 runtimeexcetpion時才回滾。  
   spring aop  異常捕獲原理:被攔截的方法需顯式拋出異常,並不能經任何處理,這樣aop代理才能捕獲到方法的異常,才能進行回滾,默認情況下aop只捕獲runtimeexception的異常,但可以通過  
    
配置來捕獲特定的異常並回滾  
  換句話說在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),這樣程序異常時才能被aop捕獲進而回滾
  解決方案: 
  方案1.例如service層處理事務,那么service中的方法中不做異常捕獲,或者在catch語句中最后增加throw new RuntimeException()語句,以便讓aop捕獲異常再去回滾,並且在service上層(webservice客戶端,view層action)要繼續捕獲這個異常並處理
  方案2.在service層方法的catch語句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();語句,手動回滾,這樣上層就無需去處理異常(現在項目的做法)

 

Spring框架的事務基礎架構代碼將默認地  在拋出運行時和unchecked exceptions時才標識事務回滾。 也就是說,當拋出一個RuntimeException 或其子類例的實例時。(Errors 也一樣 - 默認地 - 標識事務回滾。)從事務方法中拋出的Checked exceptions將  被標識進行事務回滾


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM