Spring事務管理
所謂事務管理,其實就是“按照給定的事務規則來執行提交或者回滾操作”。
三個核心接口
-
PlatformTransactionManager
: (平臺)事務管理器 -
TransactionDefinition
: 事務定義信息(事務隔離級別、傳播行為、超時、只讀、回滾規則) -
TransactionStatus
: 事務運行狀態
PlatformTransactionManager
Spring并不直接管理事務,而是提供了多種事務管理器 ,他們將事務管理的職責委托給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。
PlatformTransactionManager
接口中定義了三個方法:
Public interface PlatformTransactionManager()...{
// Return a currently active transaction or create a new one, according to the specified propagation behavior(根據指定的傳播行為,返回當前活動的事務或創建一個新事務。)
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// Commit the given transaction, with regard to its status(使用事務目前的狀態提交事務)
Void commit(TransactionStatus status) throws TransactionException;
// Perform a rollback of the given transaction(對執行的事務進行回滾)
Void rollback(TransactionStatus status) throws TransactionException;
}
我們在項目中常用的是Spring JDBC提供的DataSourceTransactionManager.
<bean id="rcsaTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="rcsaSvcDataSource" />
</bean>
<tx:annotation-driven transaction-manager="rcsaTransactionManager" />
TransactionDefinition
接口中定義的方法包括:
public interface TransactionDefinition {
// 返回事務的傳播行為
int getPropagationBehavior();
// 返回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些數據
int getIsolationLevel();
// 返回事務必須在多少秒內完成
int getTimeout();
//返回事務的名字
String getName();
// 返回是否優化為只讀事務。
boolean isReadOnly();
}
重要屬性
1) 隔離級別:
參考:并發事務可能發生的數據問題及數據庫常見隔離級別
TransactionDefinition
中定義的隔離級別常量包括:
-
TransactionDefinition.ISOLATION_DEFAULT: 使用后端數據庫默認的隔離級別,Mysql 默認采用的 REPEATABLE_READ隔離級別
Oracle 默認采用的 READ_COMMITTED隔離級別 - TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀
- TransactionDefinition.ISOLATION_READ_COMMITTED: 允許讀取并發事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發生
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生。
- TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
根據程序的并發性和安全性要求,選擇合理的事務隔離級別。
2) 事務傳播行為(為了解決業務層方法之間互相調用的事務問題):
當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啟一個新事務,并在自己的事務中運行。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
支持當前事務的情況:
- TransactionDefinition.PROPAGATION_REQUIRED: 如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_MANDATORY: 如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
不支持當前事務的情況:
- TransactionDefinition.PROPAGATION_REQUIRES_NEW: 創建一個新的事務,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NEVER: 以非事務方式運行,如果當前存在事務,則拋出異常。
其他情況:
TransactionDefinition.PROPAGATION_NESTED: 如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。
這里需要指出的是,前面的六種事務傳播行為是 Spring 從 EJB 中引入的,他們共享相同的概念。而 PROPAGATION_NESTED
是 Spring 所特有的。以 PROPAGATION_NESTED
啟動的事務內嵌于外部事務中(如果存在外部事務的話),此時,內嵌事務并不是一個獨立的事務,它依賴于外部事務的存在,只有通過外部的事務提交,才能引起內部事務的提交,嵌套的子事務不能單獨提交。如果熟悉 JDBC 中的保存點(SavePoint
)的概念,那嵌套事務就很容易理解了,其實嵌套的子事務就是保存點的一個應用,一個事務中可以包括多個保存點,每一個嵌套子事務。另外,外部事務的回滾也會導致嵌套子事務的回滾。
3) 事務超時屬性(一個事務允許執行的最長時間)
所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition
中以 int
的值來表示超時時間,其單位是秒。
4) 事務只讀屬性(對事務資源是否執行只讀操作)
事務的只讀屬性是指,對事務性資源進行只讀操作。如果確定只對事務性資源進行只讀操作,那么我們可以將事務標志為只讀的,以提高事務處理的性能。在 TransactionDefinition
中以 boolean
類型來表示該事務是否只讀。
//默認非只讀
boolean readOnly() default false;
應用場合:
如果你一次執行單條查詢語句,則沒有必要啟用事務支持,數據庫默認支持SQL執行期間的讀一致性;
如果你一次執行多條查詢語句,例如統計查詢,報表查詢,在這種場景下,多條查詢SQL必須保證整體的讀一致性,否則,在前條SQL查詢之后,后條SQL查詢之前,數據被其他用戶改變,則該次整體的統計查詢將會出現讀數據不一致的狀態,此時,應該啟用事務支持。
【注意是一次執行多次查詢來統計某些信息,這時為了保證數據整體的一致性,要用只讀事務】
對于只讀查詢,可以指定事務類型為readonly
,即只讀事務。
由于只讀事務不存在數據的修改,因此數據庫將會為只讀事務提供一些優化手段,例如Oracle對于只讀事務,不啟動回滾段,不記錄回滾log。
5) 回滾規則(定義事務回滾規則)
這些規則定義了哪些異常會導致事務回滾而哪些不會。默認情況下,事務只有遇到運行期異常時才會回滾,而在遇到檢查型異常時不會回滾(這一行為與EJB的回滾行為是一致的)。
但是你可以聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。同樣,你還可以聲明事務遇到特定的異常不回滾,即使這些異常是運行期異常。
TransactionStatus
TransactionStatus
接口用來記錄事務的狀態 該接口定義了一組方法,用來獲取或判斷事務的相應狀態信息.
PlatformTransactionManager.getTransaction(…)
方法返回一個 TransactionStatus
對象。返回的TransactionStatus
對象可能代表一個新的或已經存在的事務(如果在當前調用堆棧有一個符合條件的事務)。
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事務,值取決于事務定義的傳播機制
boolean hasSavepoint(); // 是否有恢復點,嵌套事務有用,可回滾到SavePoint
void setRollbackOnly(); // 設置為只回滾
boolean isRollbackOnly(); // 是否為只回滾
boolean isCompleted; // 是否已完成
}