Spring下面的@Transactional注解的講解


摘自: https://www.cnblogs.com/xiohao/p/4808088.html

Spring下面的@Transactional注解標志的講解

  最近在開發中對Spring中的事務標記@Transactional用的比較多,今天上網收集了一些內容,做一個簡單的總結~~~

 

  在service類前加上@Transactional,聲明這個service所有方法需要事務管理。每一個業務方法開始時都會打開一個事務。

  Spring默認情況下會對運行期例外(RunTimeException)進行事務回滾。這個例外是unchecked

  如果遇到checked意外就不回滾。

  如何改變默認規則:

  1 讓checked例外也回滾:在整個方法前加上 @Transactional(rollbackFor=Exception.class)

  2 讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)

  3 不需要事務管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

 

       如果在整個方法運行前就不會開啟事務 
       還可以加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),這樣就做成一個只讀事務,可以提高效率。

    此外需要注意: 如果異常被try{}catch{}了,事務就不回滾了,如果想讓事務回滾必須再往外拋try{}catch{throw Exception}。



       各種屬性的意義: 

     REQUIRED:業務方法需要在一個容器里運行。如果方法運行時,已經處在一個事務中,那么加入到這個事務,否則自己新建一個新的事務。 

       NOT_SUPPORTED:聲明方法不需要事務。如果方法沒有關聯到一個事務,容器不會為他開啟事務,如果方法在一個事務中被調用,該事務會被掛起,調用結束后,原先的事務會恢復執行。 

       REQUIRESNEW:不管是否存在事務,該方法總會自己發起一個新的事務。如果方法已經運行在一個事務中,則原有事務掛起,新的事務被創建。 

       MANDATORY:該方法只能在一個已經存在的事務中執行,業務方法不能發起自己的事務。如果在沒有事務的環境下被調用,容器拋出例外。 

       SUPPORTS:該方法在某個事務范圍內被調用,則方法成為該事務的一部分。如果方法在該事務范圍外被調用,該方法就在沒有事務的環境下執行。 

       NEVER:該方法絕對不能在事務范圍內執行。如果在就拋例外。只有該方法沒有關聯到任何事務,才正常執行。 

       NESTED:如果一個活動的事務存在,則運行在一個嵌套的事務中。如果沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個可以回滾的保存點。內部事務的回滾不會對外部事務造成影響。它只對

     DataSourceTransactionManager事務管理器起效。

 

 

   // 如果有事務,那么加入事務,沒有的話新建一個(不寫的情況下)
    @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)
 @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)

  

@Transactional設置:

 

propagation:事務傳播性設置,Propagation枚舉類型。Spring支持的事務傳播屬性包括7種:

   ROPAGATION_MANDATORY:方法必須在事務中執行,否則拋出異常。

    PROPAGATION_NESTED:使方法運行在嵌套事務中,否則和PROPAGATION_REQUIRED一樣。

    PROPAGATION_NEVER :當前方法永遠不在事務中運行,否則拋出異常。

    PROPAGATION_NOT_SUPPORTED:定義為當前事務不支持的方法,在該方法執行期間正在運行的事務會被暫停

    PROPAGATION_REQUIRED:當前的方法必須運行在事務中,如果沒有事務就新建一個事務。新事務和方法一起開始,隨着方法返回或者拋出異常時終止。

    PROPAGATION_REQUIRED_NEW :當前方法必須新建一個事務,如果當前的事務正在運行則暫停。

    PROPAGATION_SUPPORTS :規定當前方法支持當前事務,但是如果沒有事務在運行就使用非事務方法執行。

  

isolation:事務隔離性級別設置,Isolation枚舉類型

  ISOLATION_DEFAULT :使用數據庫默認的隔離級別

    ISOLATION_COMMITTED:允許其他事務已經提交的更新(防止臟讀取)

    ISOLATION_READ_UNCOMMITTED:允許讀取其他事務未提交的更新,會導致三個缺陷發生。執行速度最快

    ISOLATION_REPEATABLE_READ :除非事務自身更改了數據,否則事務多次讀取的數據相同(防止臟數據,多次重復讀取)

    ISOLATION_SERIALIZABLE:隔離級別最高,可以防止三個缺陷,但是速度最慢,影響性能。

  

readOnly:讀寫性事務,只讀性事務,布爾型

    對數據庫的操作中,查詢是使用最頻繁的操作,每次執行查詢時都要從數據庫中重新讀取數據,有時多次讀取的數據都是相同的,這樣的數據操作不僅浪費了系統資源,還影響了系統速度。對訪問量大的程序來說,節省這部分資源可以大大提    升系統速度。

   將事務聲明為只讀的,那么數據庫可以根據事務的特性優化事務的讀取操作

  

timeout:超時時間,單位秒

事務可能因為某種原因很長時間沒有反應,這期間可能鎖定了數據庫表,影響性能。設置超時時間,如果超過該時間,事務自動回滾。

  

rollbackFor:一組異常類的實例,遇到時必須進行回滾

 

rollbackForClassname:一組異常類的名字,遇到時必須進行回滾

 

noRollbackFor:一組異常類的實例,遇到時必須不回滾

 

noRollbackForClassname:一組異常類的名字,遇到時必須不回滾

 


實例一:事務的回滾情況

  

  1、默認情況對非運行時異常不進行回滾操作

/**
 * 運行時異常默認事務回滾
 * @throws Exception
 */
@Transactional
public void updateUser_1() throws Exception {
    Map<String,String> userMap=new HashMap<String,String>();
    userMap.put("id","1");
    userMap.put("userName","測試_01");
    userMap.put("password","654321");
    userDao.updateUserByID(userMap);
    throw new RuntimeException(); //拋出運行時異常 默認情況下回滾        事務---------------------->回滾
     
}

  

2、非運行時異常默認事務不回滾

/**
 * 非運行時異常默認事務不回滾
 * @throws Exception
 */
@Transactional
public void updateUser_3() throws Exception {
    Map<String,String> userMap=new HashMap<String,String>();
    userMap.put("id","1");
    userMap.put("userName","測試_01");
    userMap.put("password","654321");
    userDao.updateUserByID(userMap);
    throw new Exception(); //拋出非運行時異常 默認情況下不回滾        事務---------------------->不回滾
     
}

  

3、對所有異常事務都進行回滾操作

/**
 * 對所有異常事務都進行回滾操作
 * @throws Exception
 */
@Transactional(rollbackFor=Exception.class)
public void updateUser_6() throws Exception {
    Map<String,String> userMap=new HashMap<String,String>();
    userMap.put("id","1");
    userMap.put("userName","測試_01");
    userMap.put("password","654321");
    userDao.updateUserByID(userMap);
    throw new Exception(); //對所有異常事務都進行回滾操作     事務---------------------->回滾
     
}

  

4、對所有異常事務都不進行回滾操作

/**
 * 對所有異常事務都不進行回滾操作
 * @throws Exception
 */
@Transactional(noRollbackFor=Exception.class)
public void updateUser_7() throws Exception {
    Map<String,String> userMap=new HashMap<String,String>();
    userMap.put("id","1");
    userMap.put("userName","測試_01");
    userMap.put("password","654321");
    userDao.updateUserByID(userMap);
    throw new RuntimeException(); //對所有異常,事務都不進行回滾操作        事務---------------------->不回滾
     
}

  


實例二:對try{} catch{}的異常不回滾

 

/**
 * 對於try{} catch{}的異常不進行回滾
 * @throws Exception
 */
@Transactional
public void updateUser_5() throws Exception {
    Map<String,String> userMap=new HashMap<String,String>();
    userMap.put("id","1");
    userMap.put("userName","測試_01");
    userMap.put("password","654321");
    userDao.updateUserByID(userMap);
    try{
    throw new RuntimeException(); //運行時異常被捕獲,事務不回滾
    }
    catch(Exception e){
        System.err.println("運行時異常被捕獲,事務不回滾");
    }      
}

  

 

   實例三:關於嵌套事務的回滾原則

   說實話對於嵌套事務的測試,我現在仍然有一些疑問,比如說如果內部事務設置為   

     // 不管是否存在事務,都創建一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
     @Transactional(propagation=Propagation.REQUIRES_NEW) 
     那這樣的情況下,內部事務設置為Propagation.REQUIRES_NEW,外部事務設置為Propagation.REQUIRED,那么如果在內部事務執行完后之后,如果外部事務拋出了運行時異常,那么對於內部事務而言

   由於是新開的一個單獨的事務,這樣的情況下內部事務應該不受外部事務的影響而回退,但是事實上經過測試,內部事務也是回滾的。感興趣的同學可以一起探討啊~

   在網上找了一些博客,下面貼出一篇比較好嵌套事務的博客:

   http://blog.chinaunix.net/uid-10289334-id-2964925.html

 

   1、外部事務拋異常,內部事務回滾(一般來說,只要外部開啟事務,內部方法無論是否開啟事務,只要出現相應的運行時異常,事務都將進行回滾操作)

 /**
     * 測試事務的相關信息
     * @throws Exception
     */
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void updateUser() throws Exception {
        Map<String,String> userMap=new HashMap<String,String>();
        userMap.put("id","1");
        userMap.put("userName","測試_01");
        userMap.put("password","654321");
        userDao.updateUserByID(userMap);
        insideMethod_01();  //1 、異常 不捕獲                     事務---------------------->回滾
//      insideMethod_02();  //2、異常 捕獲                           事務---------------------->不回滾
        throw new RuntimeException(); //3、測試外部方法拋異常     事務---------------------->回滾
//      throw new Exception();//4、默認情況,非運行時異 常               事務---------------------->不回滾
         
    }
     
    /**
     * 事務測試的內部方法 內部方法拋運行時異常不捕獲 </br>
     *
     * 事務會對內外方法進行回滾操作  
     */
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insideMethod_01(){
        Map<String,String> userMap=new HashMap<String,String>();
        userMap.put("id","2");
        userMap.put("id","2");
        userMap.put("userName","測試_02");
        userMap.put("password","654321");
        userDao.updateUserByID(userMap);
//      int value=1/0;//運行時異常
             
    }

  

2、內部事務拋異常,外部事務回滾(一般來說,只要外部開啟事務,內部方法無論是否開啟事務如何配置,只要出現相應的運行時異常,內外事務都將進行回滾操作)

 

 /**
     * 測試事務的相關信息
     * @throws Exception
     */
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void updateUser() throws Exception {
        Map<String,String> userMap=new HashMap<String,String>();
        userMap.put("id","1");
        userMap.put("userName","測試_01");
        userMap.put("password","654321");
        userDao.updateUserByID(userMap);
        insideMethod_01();  //1 、異常 不捕獲                     事務---------------------->回滾
//      insideMethod_02();  //2、異常 捕獲                           事務---------------------->不回滾
//      throw new RuntimeException(); //3、測試外部方法拋異常     事務---------------------->回滾
//      throw new Exception();//4、默認情況,非運行時異 常               事務---------------------->不回滾
         
    }
     
    /**
     * 事務測試的內部方法 內部方法拋運行時異常不捕獲 </br>
     *
     * 事務會對內外方法進行回滾操作  
     */
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insideMethod_01(){
        Map<String,String> userMap=new HashMap<String,String>();
        userMap.put("id","2");
        userMap.put("id","2");
        userMap.put("userName","測試_02");
        userMap.put("password","654321");
        userDao.updateUserByID(userMap);
        int value=1/0;//運行時異常
             
    }

  

  注:這里還有一點在實際開發中需要注意,如果外部方法調用了遠程的接口,由於它們不在一個事務控制中,類似於分布式事務,這種情況遠程事務是無法進行回滾操作的~

 


免責聲明!

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



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