Spring事務隔離級別和傳播特性
傳播行為
事務的第一個方面是傳播行為。傳播行為定義關於客戶端和被調用方法的事務邊界。Spring定義了7中傳播行為。
傳播行為 | 意義 |
---|---|
PROPAGATION_MANDATORY [ propagation_manadatory ] |
表示該方法必須運行在一個事務中。如果當前沒有事務正在發生,將拋出一個異常 |
PROPAGATION_NESTED [ propagation_nested ] |
表示如果當前正有一個事務在進行中,則該方法應當運行在一個嵌套式事務中。被嵌套的事務可以獨立於封裝事務進行提交或回滾。如果封裝事務不存在,行為就像PROPAGATION_REQUIRES一樣。 |
PROPAGATION_NEVER [ propagation_never ] |
表示當前的方法不應該在一個事務中運行。如果一個事務正在進行,則會拋出一個異常。 |
PROPAGATION_NOT_SUPPORTED [ propagation_not_supported ] |
表示該方法不應該在一個事務中運行。如果一個現有事務正在進行中,它將在該方法的運行期間被掛起。 |
PROPAGATION_SUPPORTS [ propagation_supports ] |
表示當前方法不需要事務性上下文,但是如果有一個事務已經在運行的話,它也可以在這個事務裏運行。 |
PROPAGATION_REQUIRES_NEW [ propagation_requires_new ] |
表示當前方法必須在它自己的事務裏運行。一個新的事務將被啟動,而且如果有一個現有事務在運行的話,則將在這個方法運行期間被掛起。 |
PROPAGATION_REQUIRES [ propagation_requires ] |
表示當前方法必須在一個事務中運行。如果一個現有事務正在進行中,該方法將在那個事務中運行,否則就要開始一個新事務。 |
傳播規則回答了這樣一個問題,就是一個新的事務應該被啟動還是被掛起,或者是一個方法是否應該在事務性上下文中運行。
PROPAGATION_REQUIRED required 支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
PROPAGATION_SUPPORTS supports 支持當前事務,如果當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY mandatory 支持當前事務,如果當前沒有事務,就拋出異常。
PROPAGATION_REQUIRES_NEW requires_new 新建事務,如果當前存在事務,把當前事務掛起。
PROPAGATION_NOT_SUPPORTED not_supported 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER never 以非事務方式執行,如果當前存在事務,則拋出異常。
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也要回滾
PROPAGATION_SUPPORTS
如果當前在事務中,即以事務的形式運行,如果當前不再一個事務中,那麽就以非事務的形式運行
PROPAGATION_MANDATORY
必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常
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事務仍然可能提交。
PROPAGATION_NOT_SUPPORTED
當前不支持事務。比如ServiceA.methodA的事務級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級別是PROPAGATION_NOT_SUPPORTED ,
那麽當執行到ServiceB.methodB時,ServiceA.methodA的事務掛起,而他以非事務的狀態運行完,再繼續ServiceA.methodA的事務。
PROPAGATION_NEVER
不能在事務中運行。假設ServiceA.methodA的事務級別是PROPAGATION_REQUIRED,
而ServiceB.methodB的事務級別是PROPAGATION_NEVER ,那麽ServiceB.methodB就要拋出異常了。
PROPAGATION_NESTED
理解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區別是,PROPAGATION_REQUIRES_NEW另起一個事務,
將會與他的父事務相互獨立,而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最後回滾,他也要回滾的。
而Nested事務的好處是他有一個savepoint。
隔離級別
聲明式事務的第二個方面是隔離級別。隔離級別定義一個事務可能受其他並發事務活動活動影響的程度。另一種考慮一個事務的隔離級別的方式,是把它想象為那個事務對於事物處理數據的自私程度。
在一個典型的應用程序中,多個事務同時運行,經常會為了完成他們的工作而操作同一個數據。並發雖然是必需的,但是會導致一下問題:
- 臟讀(Dirty read)-- 臟讀發生在一個事務讀取了被另一個事務改寫但尚未提交的數據時。如果這些改變在稍後被回滾了,那麽第一個事務讀取的數據就會是無效的。
- 不可重復讀(Nonrepeatable read)-- 不可重復讀發生在一個事務執行相同的查詢兩次或兩次以上,但每次查詢結果都不相同時。這通常是由於另一個並發事務在兩次查詢之間更新了數據。
- 幻影讀(Phantom reads)-- 幻影讀和不可重復讀相似。當一個事務(T1)讀取幾行記錄後,另一個並發事務(T2)插入了一些記錄時,幻影讀就發生了。在後來的查詢中,第一個事務(T1)就會發現一些原來沒有的額外記錄。
在理想狀態下,事務之間將完全隔離,從而可以防止這些問題發生。然而,完全隔離會影響性能,因為隔離經常牽扯到鎖定在數據庫中的記錄(而且有時是鎖定完整的數據表)。侵占性的鎖定會阻礙並發,要求事務相互等待來完成工作。
考慮到完全隔離會影響性能,而且並不是所有應用程序都要求完全隔離,所以有時可以在事務隔離方面靈活處理。因此,就會有好幾個隔離級別。
隔離級別 | 含義 |
---|---|
ISOLATION_DEFAULT [default] | 使用後端數據庫默認的隔離級別。 |
ISOLATION_READ_UNCOMMITTED [read_uncommitted] | 允許讀取尚未提交的更改。可能導致臟讀、幻影讀或不可重復讀。 |
ISOLATION_READ_COMMITTED [read-committed] | 允許從已經提交的並發事務讀取。可防止臟讀,但幻影讀和不可重復讀仍可能會發生。 |
ISOLATION_REPEATABLE_READ [repeatable_read] | 對相同字段的多次讀取的結果是一致的,除非數據被當前事務本身改變。可防止臟讀和不可重復讀,但幻影讀仍可能發生。 |
ISOLATION_SERIALIZABLE [serializable] | 完全服從ACID的隔離級別,確保不發生臟讀、不可重復讀和幻影讀。這在所有隔離級別中也是最慢的,因為它通常是通過完全鎖定當前事務所涉及的數據表來完成的。 |
Dirty reads | non-repeatable reads | phantom reads | |
Serializable | 不會 | 不會 | 不會 |
REPEATABLE READ | 不會 | 不會 | 會 |
READ COMMITTED | 不會 | 會 | 會 |
Read Uncommitted | 會 | 會 | 會 |
聲明式:
使用TransactionProxyFactoryBean:
<bean id="userManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref local="userManagerTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
只讀
聲明式事務的第三個特性是它是否是一個只讀事務。如果一個事務只對後端數據庫執行讀操作,那麽該數據庫就可能利用那個事務的只讀特性,采取某些優化 措施。通過把一個事務聲明為只讀,可以給後端數據庫一個機會來應用那些它認為合適的優化措施。由於只讀的優化措施是在一個事務啟動時由後端數據庫實施的, 因此,只有對於那些具有可能啟動一個新事務的傳播行為(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED)的方法來說,將事務聲明為只讀才有意義。
此外,如果使用Hibernate作為持久化機制,那麽把一個事務聲明為只讀,將使Hibernate的flush模式被設置為FLUSH_NEVER。這就告訴Hibernate避免和數據庫進行不必要的對象同步,從而把所有更新延遲到事務的結束。
事務超時
為了使一個應用程序很好地執行,它的事務不能運行太長時間。因此,聲明式事務的下一個特性就是它的超時。
假設事務的運行時間變得格外的長,由於事務可能涉及對後端數據庫的鎖定,所以長時間運行的事務會不必要地占用數據庫資源。這時就可以聲明一個事務在特定秒數後自動回滾,不必等它自己結束。
由於超時時鐘在一個事務啟動的時候開始的,因此,只有對於那些具有可能啟動一個新事務的傳播行為(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、ROPAGATION_NESTED)的方法來說,聲明事務超時才有意義。
回滾規則
事務五邊形的對後一個邊是一組規則,它們定義哪些異常引起回滾,哪些不引起。在默認設置下,事務只在出現運行時異常(runtime exception)時回滾,而在出現受檢查異常(checked exception)時不回滾(這一行為和EJB中的回滾行為是一致的)。
不過,也可以聲明在出現特定受檢查異常時像運行時異常一樣回滾。同樣,也可以聲明一個事務在出現特定的異常時不回滾,即使那些異常是運行時一場。
Spring事務隔離級別和傳播特性