1. 程式人生 > >spring事物隔離級別,傳播特性由淺入深,@Transactional註解

spring事物隔離級別,傳播特性由淺入深,@Transactional註解

本人在學習spring註解時通過網上各個部落格瀏覽,把好多人的結論總結到一起,喜歡就看看吧。

spring支援程式設計式事務管理和宣告式事務管理兩種方式。

        程式設計式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於程式設計式事務管理,spring推薦使用TransactionTemplate。

        宣告式事務管理建立在AOP之上的。其本質是對方法前後進行攔截,然後在目標方法開始之前建立或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。宣告式事務最大的優點就是不需要通過程式設計的方式管理事務,這樣就不需要在業務邏輯程式碼中摻雜事務管理的程式碼,只需在配置檔案中做相關的事務規則宣告(或通過基於@Transactional註解的方式),便可以將事務規則應用到業務邏輯中。

       顯然宣告式事務管理要優於程式設計式事務管理,這正是spring倡導的非侵入式的開發方式。宣告式事務管理使業務程式碼不受汙染,一個普通的POJO物件,只要加上註解就可以獲得完全的事務支援。和程式設計式事務相比,宣告式事務唯一不足地方是,後者的最細粒度只能作用到方法級別,無法做到像程式設計式事務那樣可以作用到程式碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的程式碼塊獨立為方法等等。

         宣告式事務管理也有兩種常用的方式,一種是基於tx和aop名字空間的xml配置檔案,另一種就是基於@Transactional註解。顯然基於註解的方式更簡單易用,更清爽。

spring事務特性

spring所有的事務管理策略類都繼承自org.springframework.transaction.PlatformTransactionManager介面

其中TransactionDefinition介面定義以下特性:

事務隔離級別

  隔離級別是指若干個併發的事務之間的隔離程度。TransactionDefinition 介面中定義了五個表示隔離級別的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:這是預設值,表示使用底層資料庫的預設隔離級別。對大部分資料庫而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的資料。該級別不能防止髒讀,不可重複讀和幻讀,因此很少使用該隔離級別。比如PostgreSQL實際上並沒有此級別。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另一個事務已經提交的資料。該級別可以防止髒讀,這也是大多數情況下的推薦值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程中可以多次重複執行某個查詢,並且每次返回的記錄都相同。該級別可以防止髒讀和不可重複讀。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止髒讀、不可重複讀以及幻讀。但是這將嚴重影響程式的效能。通常情況下也不會用到該級別。

事務傳播行為

這裡先粗略的講講,詳細的在下面。

      所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。在TransactionDefinition定義中包括瞭如下幾個表示傳播行為的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則建立一個新的事務。這是預設值。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,如果當前存在事務,則把當前事務掛起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續執行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式執行,如果當前存在事務,則把當前事務掛起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則丟擲異常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則丟擲異常。
  • TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則建立一個事務作為當前事務的巢狀事務來執行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。

事務超時

 所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。

  預設設定為底層事務系統的超時值,如果底層資料庫事務系統沒有設定超時值,那麼就是none,沒有超時限制。

事務只讀屬性

只讀事務用於客戶程式碼只讀但不修改資料的情形,只讀事務用於特定情景下的優化,比如使用Hibernate的時候。 預設為讀寫事務。

        “只讀事務”並不是一個強制選項,它只是一個“暗示”,提示資料庫驅動程式和資料庫系統,這個事務並不包含更改資料的操作,那麼JDBC驅動程式和資料庫就有可能根據這種情況對該事務進行一些特定的優化,比方說不安排相應的資料庫鎖,以減輕事務對資料庫的壓力,畢竟事務也是要消耗資料庫的資源的。 

但是你非要在“只讀事務”裡面修改資料,也並非不可以,只不過對於資料一致性的保護不像“讀寫事務”那樣保險而已。 

因此,“只讀事務”僅僅是一個性能優化的推薦配置而已,並非強制你要這樣做不可

spring事務回滾規則

     指示spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內丟擲異常。spring事務管理器會捕捉任何未處理的異常,然後依據規則決定是否回滾丟擲異常的事務。

        預設配置下,spring只有在丟擲的異常為執行時unchecked異常時才回滾該事務,也就是丟擲的異常為RuntimeException的子類(Errors也會導致事務回滾),而丟擲checked異常則不會導致事務回滾。可以明確的配置在丟擲那些異常時回滾事務,包括checked異常。也可以明確定義那些異常丟擲時不回滾事務。還可以程式設計性的通過setRollbackOnly()方法來指示一個事務必須回滾,在呼叫完setRollbackOnly()後你所能執行的唯一操作就是回滾。

事物的傳播行為(敘述)

       我們都知道事務的概念,那麼事務的傳播特性是什麼呢?(此處著重介紹傳播特性的概念,關於傳播特性的相關配置就不介紹了,可以檢視spring的官方文件) 
       在我們用SSH開發專案的時候,我們一般都是將事務設定在Service層 那麼當我們呼叫Service層的一個方法的時候它能夠保證我們的這個方法中執行的所有的對資料庫的更新操作保持在一個事務中,在事務層裡面呼叫的這些方法要麼全部成功,要麼全部失敗。那麼事務的傳播特性也是從這裡說起的。 
       如果你在你的Service層的這個方法中,除了呼叫了Dao層的方法之外,還呼叫了本類的其他的Service方法,那麼在呼叫其他的Service方法的時候,這個事務是怎麼規定的呢,我必須保證我在我方法裡掉用的這個方法與我本身的方法處在同一個事務中,否則如果保證事物的一致性。事務的傳播特性就是解決這個問題的,“事務是會傳播的”在Spring中有針對傳播特性的多種配置我們大多數情況下只用其中的一種:PROPGATION_REQUIRED:這個配置項的意思是說當我呼叫service層的方法的時候開啟一個事務(具體呼叫那一層的方法開始建立事務,要看你的aop的配置),那麼在呼叫這個service層裡面的其他的方法的時候,如果當前方法產生了事務就用當前方法產生的事務,否則就建立一個新的事務。這個工作使由Spring來幫助我們完成的。 
        以前沒有Spring幫助我們完成事務的時候我們必須自己手動的控制事務,例如當我們專案中僅僅使用hibernate,而沒有整合進spring的時候,我們在一個service層中呼叫其他的業務邏輯方法,為了保證事物必須也要把當前的hibernate session傳遞到下一個方法中,或者採用ThreadLocal的方法,將session傳遞給下一個方法,其實都是一個目的。現在這個工作由spring來幫助我們完成,就可以讓我們更加的專注於我們的業務邏輯。而不用去關心事務的問題。 

        預設情況下當發生RuntimeException的情況下,事務才會回滾,所以要注意一下 如果你在程式發生錯誤的情況下,有自己的異常處理機制定義自己的Exception,必須從RuntimeException類繼承 這樣事務才會回滾!

傳播行為

含義

PROPAGATION_REQUIREDXML檔案中為REQUIRED)

表示當前方法必須在一個具有事務的上下文中執行,如有客戶端有事務在進行,那麼被呼叫端將在該事務中執行,否則的話重新開啟一個事務。(如果被呼叫端發生異常,那麼呼叫端和被呼叫端事務都將回滾

PROPAGATION_SUPPORTS(XML檔案中為SUPPORTS

表示當前方法不必需要具有一個事務上下文,但是如果有一個事務的話,它也可以在這個事務中執行

PROPAGATION_MANDATORY(XML檔案中為MANDATORY

表示當前方法必須在一個事務中執行,如果沒有事務,將丟擲異常

PROPAGATION_NESTED(XML檔案中為NESTED)

表示如果當前方法正有一個事務在執行中,則該方法應該執行在一個巢狀事務中,被巢狀的事務可以獨立於被封裝的事務中進行提交或者回滾。如果封裝事務存在,並且外層事務丟擲異常回滾,那麼內層事務必須回滾,反之,內層事務並不影響外層事務。如果封裝事務不存在,則同PROPAGATION_REQUIRED的一樣

PROPAGATION_NEVERXML檔案中為NEVER)

表示當方法務不應該在一個事務中執行,如果存在一個事務,則丟擲異常

PROPAGATION_REQUIRES_NEW(XML檔案中為REQUIRES_NEW

表示當前方法必須執行在它自己的事務中。一個新的事務將啟動,而且如果有一個現有的事務在執行的話,則這個方法將在執行期被掛起,直到新的事務提交或者回滾才恢復執行

PROPAGATION_NOT_SUPPORTEDXML檔案中為NOT_SUPPORTED

表示該方法不應該在一個事務中執行。如果有一個事務正在執行,他將在執行期被掛起,直到這個事務提交或者回滾才恢復執行 

例子講解以上七中事務傳播機制

假設有類A的方法methodB(),有類B的方法methodB().

1)     PROPAGATION_REQUIRED

如果B的方法methodB()的事務傳播特性是propagation_required,那麼如下圖

A.methodA()呼叫BmethodB()方法,那麼如果A的方法包含事務,則B的方法則不從新開啟事務,

1、  如果BmethodB()丟擲異常,AmethodB()沒有捕獲,則AB的事務都會回滾;

2、   如果BmethodB()執行期間異常會導致BmethodB()的回滾,A如果捕獲了異常,並正常提交事務,則會發生Transaction rolled back because it has been marked as rollback-only的異常。

3、  如果A的methodA()執行期間異常,則A和B的Method的事務都會被回滾

2)     PROPAGATION_SUPPORTS

如果B的方法methodB()的事務傳播特性是propagation_supports,麼如下圖

A.methodA()呼叫BmethodB()方法,那麼如果A的方法包含事務,則B執行在此事務環境中,如果A的方法不包含事務,則B執行在非事務環境;

1、如果A沒有事務,則AB的執行出現異常都不會回滾。

2、如果A有事務,Amethod方法執行丟擲異常,B.methodBA.methodA都會回滾。

3、如果A有事務,B.method丟擲異常,B.methodBA.methodA都會回滾,如果A捕獲了B.method丟擲的異常,則會出現異常Transactionrolled back because it has been marked as rollback-only

3)     PROPAGATION_MANDATORY

表示當前方法必須在一個事務中執行,如果沒有事務,將丟擲異常,如下圖呼叫關係:

B.methodB()事務傳播特性定義為:PROPAGATION_MANDATORY

1、如果Amethoda()方法沒有事務執行環境,則BmethodB()執行的時候會報如下異常:No existingtransaction found for transaction marked with propagation 'mandatory'

2、如果AMethoda()方法有事務並且執行過程中丟擲異常,則A.methoda()和B.methodb()執行的操作被回滾;

3、如果Amethoda()方法有事務,則B.methodB()丟擲異常時,Amethoda()B.methodB()都會被回滾;如果A捕獲了B.method丟擲的異常,則會出現異常Transaction rolled back because ithas been marked as rollback-only

4)     PROPAGATION_NESTED

如有一下方法呼叫關係,如圖:

BmethodB()定義的事務為PROPAGATION_NESTED

1、        如果AMethodA()不存在事務,則BmethodB()執行在一個新的事務中,B.method()丟擲的異常,B.methodB()回滾,A.methodA()不回滾;如果A.methoda()丟擲異常,則A.methodA()B.methodB()操作不回。

2、        如果AmethodA()存在事務,則Amethoda()丟擲異常,則Amethoda()BMethodb()都會被回滾;

3、        如果AMethodA()存在事務,則BmethodB()丟擲異常,B.methodB()回滾,如果A不捕獲異常,則A.methodA()B.methodB()都會回滾,如果A捕獲異常,則B.methodB()回滾,A不回滾;

5PROPAGATION_NEVER

表示事務傳播特性定義為PROPAGATION_NEVER的方法不應該執行在一個事務環境中

有如下呼叫關係:

如果B.methodB()的事務傳播特性被定義為PROPAGATION_NEVER,則如果A.methodA()方法存在事務,則會出現異常Existingtransaction found for transaction marked with propagation 'never'

6PROPAGATION_REQUIRES_NEW

表示事務傳播特性定義為PROPAGATION_REQUIRES_NEW的方法需要執行在一個新的事務中。

如有一下呼叫關係:B.methodB()事務傳播特性為PROPAGATION_REQUIRES_NEW.

1、        如果A存在事務,A.methodA()丟擲異常,A.methodA()的事務被回滾,但B.methodB()事務不受影響;如果B.methodB()丟擲異常,A不捕獲的話,A.methodA()B.methodB()的事務都會被回滾。如果A捕獲的話,A.methodA()的事務不受影響但B.methodB()的事務回滾。

7) PROPAGATION_NOT_SUPPORTED

表示該方法不應該在一個事務中執行。如果有一個事務正在執行,他將在執行期被掛起,直到這個事務提交或者回滾才恢復執行。

如有一下呼叫關係圖: