spring之事務回滾技巧


 

1、遇到的問題

當我們一個方法里面有多個數據庫保存操作的時候,中間的數據庫操作發生的錯誤。偽代碼如下:

public method() {
   Dao1.save(Person1);
   Dao1.save(Person2);
   Dao1.save(Person2);
   //假如這句發生了錯誤,前面的兩個對象會被保存到數據庫中
   Dao1.save(Person2);
}

 

期待的情況:發生錯誤之前的所有數據庫保存操作都回滾,即不保存

 

正常情況:前面的數據庫操作會被執行,而發生數據庫操作錯誤開始及之后的所有的數據保存操作都將失敗。這樣子應該都不是我們要的結果吧。

 

當遇到這種情況,我們就可以使用Spring的事務解決這個問題。

 

2、異常的一些基本知識

 

1) 異常的架構

Throwable為基類,Error和Exception繼承Throwable,RuntimeException和IOException等繼承Exception。

 

Error和RuntimeException及其子類成為未檢查異常(unchecked),其它異常成為已檢查異常(checked)。 

 

 

2)Error異常

Error表示程序在運行期間出現了十分嚴重、不可恢復的錯誤,在這種情況下應用程序只能中止運行,例如JAVA 虛擬機出現錯誤。Error是一種unchecked Exception,編譯器不會檢查Error是否被處理,在程序中不用捕獲Error類型的異常。一般情況下,在程序中也不應該拋出Error類型的異常。

 

3)RuntimeException異常

Exception異常包括RuntimeException異常和其他非RuntimeException的異常。

  

RuntimeException 是一種Unchecked Exception,即表示編譯器不會檢查程序是否對RuntimeException作了處理,在程序中不必捕獲RuntimException類型的異常,也不必在方法體聲明拋出RuntimeException類。

 

RuntimeException發生的時候,表示程序中出現了編程錯誤,所以應該找出錯誤修改程序,而不是去捕獲RuntimeException。

 

4)Checked Exception異常

Checked Exception異常,這也是在編程中使用最多的Exception,所有繼承自Exception並且不是RuntimeException的異常都是checked Exception,上圖中的IOException和ClassNotFoundException。

 

JAVA 語言規定必須對checked Exception作處理,編譯器會對此作檢查,要么在方法體中聲明拋出checked Exception,要么使用catch語句捕獲checked Exception進行處理,不然不能通過編譯。

 

3、實例

這里使用的事務配置如下:


   bean id='transactionManager' class='org.springframework.orm.jpa.JpaTransactionManager'>
       property name='entityManagerFactory' ref='entityManagerFactory'/>
   bean>
   
   
   tx:annotation-driven transaction-manager='transactionManager' proxy-target-class='true' />

 

在spring的配置文件中,如果數據源的defaultAutoCommit設置為True了,那么方法中如果自己捕獲了異常,事務是不會回滾的,如果沒有自己捕獲異常則事務會回滾,如下例

 

比如配置文件里有這么條記錄

'dataSource' class='com.alibaba.druid.pool.DruidDataSource'>

'xxx' value='xxx'/>

'xxx' value='xxx'/>

....
'defaultAutoCommit' value='true' />

 

可能你會發現你並沒有配置這個參數,是不是他就不會自動提交呢?答案是不是的,我這里是使用了com.alibaba.druid.pool.DruidDataSource作為數據庫連接池,默認的defaultAutoCommit就是true,可以看下面的源碼

 

 

 

那么現在有兩個情況

  

情況1:如果沒有在程序中手動捕獲異常

@Transactional(rollbackOn = { Exception.class })  
public void test() throws Exception {  
    doDbStuff1();  
    doDbStuff2();
//假如這個操作數據庫的方法會拋出異常,
//現在方法doDbStuff1()對數據庫的操作   會回滾。  
}

 

情況2:如果在程序中自己捕獲了異常

@Transactional(rollbackOn = { Exception.class })  
public void test() {  
    try {  
       doDbStuff1();  
       doDbStuff2();
//假如這個操作數據庫的方法會拋出異常,
//現在方法doDbStuff1()對數據庫的操作  不會回滾。  
    } catch (Exception e) {  
          e.printStackTrace();    
    }  
}

 

現在如果我們需要手動捕獲異常,並且也希望拋異常的時候能回滾腫么辦呢?

  

下面這樣寫就好了,手動回滾事務:

@Transactional(rollbackOn = { Exception.class })  
public void test() {  
   try {  
      doDbStuff1();  
      doDbStuff2();  
   } catch (Exception e) {  
        e.printStackTrace();    
        TransactionAspectSupport.currentTransactionStatus()
        .setRollbackOnly();
    //就是這一句了,加上之后,如果doDbStuff2()拋了異常,
    //doDbStuff1()是會回滾的  
   }  
}


免責聲明!

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



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