Spring 事務之@Transactional

在業務代碼中,有如下兩種情況,比如:
throw new RuntimeException("xxxxxxxxxxxx"); 事務回滾
throw new Exception("xxxxxxxxxxxx"); 事務沒有回滾

 

 

1).Spring的AOP即聲明式事務管理默認是針對unchecked exception回滾。也就是默認對RuntimeException()異常或是其子類進行事務回滾;checked異常,即Exception可try{}捕獲的不會回滾,如果使用try-catch捕獲拋出的unchecked異常後沒有在catch塊中採用頁面硬編碼的方式使用spring api對事務做顯式的回滾,則事務不會回滾, “將異常捕獲,並且在catch塊中不對事務做顯式提交=生吞掉異常” ,要想捕獲非運行時異常則需要如下配置:

解決辦法:
1.在針對事務的類中拋出RuntimeException異常,而不是拋出Exception。
2.在txAdive中增加rollback-for,裏面寫自己的exception,例如自己寫的exception:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
     <tx:method name="*" rollback-for="com.cn.untils.exception.XyzException"/>
   </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>

 
2).spring的事務邊界是在調用業務方法之前開始的,業務方法執行完畢之後來執行commit or rollback(Spring默認取決於是否拋出runtime異常).
 如果拋出runtime exception 並在你的業務方法中沒有catch到的話,事務會回滾。 
 一般不需要在業務方法中catch異常,如果非要catch,在做完你想做的工作後(比如關閉文件等)一定要拋出runtime exception,否則spring會將你的操作commit,這樣就會產生髒數據.所以你的catch代碼是畫蛇添足。
 
如:
try {  
    //bisiness logic code  
} catch(Exception e) {  
    //handle the exception  
}  

 由此可以推知,在spring中如果某個業務方法被一個 整個包裹起來,則這個業務方法也就等於脫離了spring事務的管理,因爲沒有任何異常會從業務方法中拋出!全被捕獲併吞掉,導致spring異常拋出觸發事務回滾策略失效。
 不過,如果在catch代碼塊中採用頁面硬編碼的方式使用spring api對事務做顯式的回滾,這樣寫也未嘗不可。
 
 3).基於註解的事務:

 Transactional的異常控制,默認是Check Exception 不回滾,unCheck Exception回滾
 如果配置了rollbackFor 和 noRollbackFor 且兩個都是用同樣的異常,那麼遇到該異常,還是回滾
 rollbackFor 和noRollbackFor 配置也許不會含蓋所有異常,對於遺漏的按照Check Exception 不回滾,unCheck Exception回滾


默認遇到throw new RuntimeException("...");會回滾
需要捕獲的throw new Exception("...");不會回滾

// 指定回滾
@Transactional(rollbackFor=Exception.class) 
    public void methodName() {
       // 不會回滾
       throw new Exception("...");
    } 
//指定不回滾
@Transactional(noRollbackFor=Exception.class)
    public ItimDaoImpl getItemDaoImpl() {
        // 會回滾
        throw new RuntimeException("註釋");
    } 

    // 如果有事務,那麼加入事務,沒有的話新建一個(不寫的情況下)

    @Transactional(propagation=Propagation.REQUIRED) 
    // 容器不爲這個方法開啓事務
    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    // 不管是否存在事務,都創建一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
    @Transactional(propagation=Propagation.REQUIRES_NEW) 
    // 必須在一個已有的事務中執行,否則拋出異常
    @Transactional(propagation=Propagation.MANDATORY)
    // 必須在一個沒有的事務中執行,否則拋出異常(與Propagation.MANDATORY相反)
    @Transactional(propagation=Propagation.NEVER) 
    // 如果其他bean調用這個方法,在其他bean中聲明事務,那就用事務.如果其他bean沒有聲明事務,那就不用事務.
    @Transactional(propagation=Propagation.SUPPORTS) 
    
    /*
    public void methodName(){
       // 本類的修改方法 1
       update();
       // 調用其他類的修改方法

       otherBean.update();
       // 本類的修改方法 2
       update();
    }
    other失敗了不會影響 本類的修改提交成功
    本類update的失敗,other也失敗
    */
@Transactional(propagation=Propagation.NESTED) 
// readOnly=true只讀,不能更新,刪除 
@Transactional (propagation = Propagation.REQUIRED,readOnly=true) 
// 設置超時時間
@Transactional (propagation = Propagation.REQUIRED,timeout=30)
// 設置數據庫隔離級別
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)

轉自:http://blog.sina.com.cn/s/blog_6ac4c6cb01018pbl.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章