Spring下面的@Transactional註解的講解 Spring下面的@Transactional註解標誌的講解

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

Spring下面的@Transactional註解標誌的講解

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

 

  在service類前加上@Transactional,聲明這個service全部方法須要事務管理。每個業務方法開始時都會打開一個事務。數據庫

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

  若是遇到checked意外就不回滾。ide

  如何改變默認規則:post

  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),這樣就作成一個只讀事務,能夠提升效率。ui

    此外須要注意: 若是異常被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:一組異常類的名字,遇到時必須不回滾

 


實例一:事務的回滾狀況

  

  一、默認狀況對非運行時異常不進行回滾操做

/**
 * 運行時異常默認事務回滾
 * @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(); //拋出運行時異常 默認狀況下回滾        事務---------------------->回滾
     
}

  

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

/**
 * 非運行時異常默認事務不回滾
 * @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(); //拋出非運行時異常 默認狀況下不回滾        事務---------------------->不回滾
     
}

  

三、對全部異常事務都進行回滾操做

/**
 * 對全部異常事務都進行回滾操做
 * @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(); //對全部異常事務都進行回滾操做     事務---------------------->回滾
     
}

  

四、對全部異常事務都不進行回滾操做

/**
 * 對全部異常事務都不進行回滾操做
 * @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

 

   一、外部事務拋異常,內部事務回滾(通常來講,只要外部開啓事務,內部方法不管是否開啓事務,只要出現相應的運行時異常,事務都將進行回滾操做)

 /**
     * 測試事務的相關信息
     * @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();  //二、異常 捕獲                           事務---------------------->不回滾
        throw new RuntimeException(); //三、測試外部方法拋異常     事務---------------------->回滾
//      throw new Exception();//四、默認狀況,非運行時異 常               事務---------------------->不回滾
         
    }
     
    /**
     * 事務測試的內部方法 內部方法拋運行時異常不捕獲 </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;//運行時異常
             
    }

  

二、內部事務拋異常,外部事務回滾(通常來講,只要外部開啓事務,內部方法不管是否開啓事務如何配置,只要出現相應的運行時異常,內外事務都將進行回滾操做)

 

 /**
     * 測試事務的相關信息
     * @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();  //二、異常 捕獲                           事務---------------------->不回滾
//      throw new RuntimeException(); //三、測試外部方法拋異常     事務---------------------->回滾
//      throw new Exception();//四、默認狀況,非運行時異 常               事務---------------------->不回滾
         
    }
     
    /**
     * 事務測試的內部方法 內部方法拋運行時異常不捕獲 </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;//運行時異常
             
    }

  

  注:這裏還有一點在實際開發中須要注意,若是外部方法調用了遠程的接口,因爲它們不在一個事務控制中,相似於分佈式事務,這種狀況遠程事務是沒法進行回滾操做的~

相關文章
相關標籤/搜索