spring @Transactional註解參數詳解

參照https://www.cnblogs.com/caoyc/p/5632963.html

spring @Transactional註解參數詳解

事物註解方式: @Transactional

當標於類前時, 標示類中所有方法都進行事物處理 , 例子:

1 @Transactional public class TestServiceBean implements TestService {}

當類中某些方法不需要事物時:

複製代碼

 1 @Transactional  
 2 public class TestServiceBean implements TestService { 
 3     private TestDao dao; 
 4     public void setDao(TestDao dao) { 
 5         this.dao = dao; 
 6     } 
 7     @Transactional(propagation =Propagation.NOT_SUPPORTED)
 8     public List getAll() { 
 9         return null; 
10     } 
11 }

複製代碼

 

事物傳播行爲介紹: 

  @Transactional(propagation=Propagation.REQUIRED) :如果有事務, 那麼加入事務, 沒有的話新建一個(默認情況下)
  @Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不爲這個方法開啓事務
  @Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事務,都創建一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
  @Transactional(propagation=Propagation.MANDATORY) :必須在一個已有的事務中執行,否則拋出異常
  @Transactional(propagation=Propagation.NEVER) :必須在一個沒有的事務中執行,否則拋出異常(與Propagation.MANDATORY相反)
  @Transactional(propagation=Propagation.SUPPORTS) :如果其他bean調用這個方法,在其他bean中聲明事務,那就用事務.如果其他bean沒有聲明事務,那就不用事務.

一、Propagation (事務的傳播屬性)

Propagation :  key屬性確定代理應該給哪個方法增加事務行爲。這樣的屬性最重要的部份是傳播行爲。有以下選項可供使用:PROPAGATION_REQUIRED--支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
PROPAGATION_SUPPORTS--支持當前事務,如果當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY--支持當前事務,如果當前沒有事務,就拋出異常。
PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。
PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則拋出異常。

1: PROPAGATION_REQUIRED
加入當前正要執行的事務不在另外一個事務裏,那麼就起一個新的事務
比如說,ServiceB.methodB的事務級別定義爲PROPAGATION_REQUIRED, 那麼由於執行ServiceA.methodA的時候,
ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB看到自己已經運行在ServiceA.methodA
的事務內部,就不再起新的事務。而假如ServiceA.methodA運行的時候發現自己沒有在事務中,他就會爲自己分配一個事務。
這樣,在ServiceA.methodA或者在ServiceB.methodB內的任何地方出現異常,事務都會被回滾。即使ServiceB.methodB的事務已經被
提交,但是ServiceA.methodA在接下來fail要回滾,ServiceB.methodB也要回滾

2: PROPAGATION_SUPPORTS
如果當前在事務中,即以事務的形式運行,如果當前不再一個事務中,那麼就以非事務的形式運行


3: PROPAGATION_MANDATORY
必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常

4: PROPAGATION_REQUIRES_NEW
這個就比較繞口了。 比如我們設計ServiceA.methodA的事務級別爲PROPAGATION_REQUIRED,ServiceB.methodB的事務級別爲PROPAGATION_REQUIRES_NEW,
那麼當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成以後,
他才繼續執行。他與PROPAGATION_REQUIRED 的事務區別在於事務的回滾程度了。因爲ServiceB.methodB是新起一個事務,那麼就是存在
兩個不同的事務。如果ServiceB.methodB已經提交,那麼ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。如果ServiceB.methodB失敗回滾,
如果他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務仍然可能提交。

5: PROPAGATION_NOT_SUPPORTED
當前不支持事務。比如ServiceA.methodA的事務級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級別是PROPAGATION_NOT_SUPPORTED ,
那麼當執行到ServiceB.methodB時,ServiceA.methodA的事務掛起,而他以非事務的狀態運行完,再繼續ServiceA.methodA的事務。

6: PROPAGATION_NEVER
不能在事務中運行。假設ServiceA.methodA的事務級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務級別是PROPAGATION_NEVER ,
那麼ServiceB.methodB就要拋出異常了。

7: PROPAGATION_NESTED
理解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區別是,PROPAGATION_REQUIRES_NEW另起一個事務,將會與他的父事務相互獨立,
而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最後回滾,他也要回滾的。
而Nested事務的好處是他有一個savepoint。
*****************************************
ServiceA {

/**
* 事務屬性配置爲 PROPAGATION_REQUIRED
*/
void methodA() {
try {
//savepoint
ServiceB.methodB(); //PROPAGATION_NESTED 級別
} catch (SomeException) {
// 執行其他業務, 如 ServiceC.methodC();
}
}

}
********************************************
也就是說ServiceB.methodB失敗回滾,那麼ServiceA.methodA也會回滾到savepoint點上,ServiceA.methodA可以選擇另外一個分支,比如
ServiceC.methodC,繼續執行,來嘗試完成自己的事務。
但是這個事務並沒有在EJB標準中定義。

Spring事務的隔離級別
 1. ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.
      另外四個與JDBC的隔離級別相對應
 2. ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的數據。
      這種隔離級別會產生髒讀,不可重複讀和幻像讀。
 3. ISOLATION_READ_COMMITTED: 保證一個事務修改的數據提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據
 4. ISOLATION_REPEATABLE_READ: 這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。
      它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下面的情況產生(不可重複讀)。
 5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理爲順序執行。
      除了防止髒讀,不可重複讀外,還避免了幻像讀。

什麼是髒數據,髒讀,不可重複讀,幻覺讀?
 髒讀: 指當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,
     另外一個事務也訪問這個數據,然後使用了這個數據。因爲這個數據是還沒有提交的數據, 那麼另外一
     個事務讀到的這個數據是髒數據,依據髒數據所做的操作可能是不正確的。
    
 不可重複讀: 指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。
             那麼,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,那麼第一個事務兩次讀到的數據
             可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱爲是不可重複讀。
            
 幻覺讀: 指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及
         到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,
         以後就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。

事物超時設置:

  @Transactional(timeout=30) //默認是30秒

 

事務隔離級別:

  @Transactional(isolation = Isolation.READ_UNCOMMITTED):讀取未提交數據(會出現髒讀, 不可重複讀) 基本不使用
  @Transactional(isolation = Isolation.READ_COMMITTED):讀取已提交數據(會出現不可重複讀和幻讀)
  @Transactional(isolation = Isolation.REPEATABLE_READ):可重複讀(會出現幻讀)
  @Transactional(isolation = Isolation.SERIALIZABLE):串行化

  MYSQL: 默認爲REPEATABLE_READ級別
  SQLSERVER: 默認爲READ_COMMITTED

髒讀 : 一個事務讀取到另一事務未提交的更新數據
不可重複讀 : 在同一事務中, 多次讀取同一數據返回的結果有所不同, 換句話說, 
後續讀取可以讀到另一事務已提交的更新數據. 相反, "可重複讀"在同一事務中多次
讀取數據時, 能夠保證所讀數據一樣, 也就是後續讀取不能讀到另一事務已提交的更新數據
幻讀 : 一個事務讀到另一個事務已提交的insert數據

 

@Transactional註解中常用參數說明

參數名稱

功能描述

readOnly

該屬性用於設置當前事務是否爲只讀事務,設置爲true表示只讀,false則表示可讀寫,默認值爲false。例如:@Transactional(readOnly=true)

rollbackFor

該屬性用於設置需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。例如:

指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)

指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

rollbackForClassName

該屬性用於設置需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。例如:

指定單一異常類名稱:@Transactional(rollbackForClassName="RuntimeException")

指定多個異常類名稱:@Transactional(rollbackForClassName={"RuntimeException","Exception"})

noRollbackFor

該屬性用於設置不需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。例如:

指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)

指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

該屬性用於設置不需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。例如:

指定單一異常類名稱:@Transactional(noRollbackForClassName="RuntimeException")

指定多個異常類名稱:

@Transactional(noRollbackForClassName={"RuntimeException","Exception"})

propagation

該屬性用於設置事務的傳播行爲,具體取值可參考表6-7。

例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

isolation

該屬性用於設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務併發的情況,通常使用數據庫的默認隔離級別即可,基本不需要進行設置

timeout

該屬性用於設置事務的超時秒數,默認值爲-1表示永不超時

 

 

注意的幾點:


  1、@Transactional 只能被應用到public方法上, 對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能.

  2、用 spring 事務管理器,由spring來負責數據庫的打開,提交,回滾.默認遇到運行期例外(throw new RuntimeException("註釋");)會回滾,即遇到不受檢查(unchecked)的例外時回滾;而遇到需要捕獲的例外(throw new Exception("註釋");)不會回滾,即遇到受檢查的例外(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢查例外或說受檢查異常)時,需我們指定方式來讓事務回滾要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .如果讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)
如下:

複製代碼

1 @Transactional(rollbackFor=Exception.class) //指定回滾,遇到異常Exception時回滾
2 public void methodName() {
3    throw new Exception("註釋");
4 }
5 @Transactional(noRollbackFor=Exception.class)//指定不回滾,遇到運行期例外(throw new RuntimeException("註釋");)會回滾
6 public ItimDaoImpl getItemDaoImpl() {
7    throw new RuntimeException("註釋");
8 }

複製代碼

  3、@Transactional 註解應該只被應用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 但是這個被註解的方法將不會展示已配置的事務設置。


  4、@Transactional 註解可以被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 註解的出現不足於開啓事務行爲,它僅僅 是一種元數據,能夠被可以識別 @Transactional 註解和上述的配置適當的具有事務行爲的beans所使用。上面的例子中,其實正是 元素的出現 開啓 了事務行爲。


  5、Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 註解,而不要使用在類所要實現的任何接口上。你當然可以在接口上使用 @Transactional 註解,但是這將只能當你設置了基於接口的代理時它才生效。因爲註解是不能繼承的,這就意味着如果你正在使用基於類的代理時,那麼事務的設置將不能被基於類的代理所識別,而且對象也將不會被事務代理所包裝(將被確認爲嚴重的)。因此,請接受Spring團隊的建議並且在具體的類上使用 @Transactional 註解。

分類: Spring

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