Spring事務管理
Spring事務管理接口
-
PlatformTransactionManager: (平臺)事務管理器
-
TransactionDefinition: 事務定義信息(事務隔離級別、傳播行爲、超時、只讀、回滾規則)
-
TransactionStatus: 事務運行狀態
所謂事務管理,其實就是“按照給定的事務規則來執行提交或者回滾操作”。
PlatformTransactionManager接口介紹
Spring並不直接管理事務,而是提供了多種事務管理器。Spring爲各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,但是具體的實現就是各個平臺自己的事情了。
接口內方法
public interface PlatformTransactionManager(){
// 根據指定的傳播行爲,返回當前活動的事務或創建一個新事務
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 使用事務目前的狀態提交事務
Void commit(TransactionStatus status) throws TransactionException;
// 對執行的事務進行回滾
Void rollback(TransactionStatus status) throws TransactionException;
}
我們剛剛也說了Spring中PlatformTransactionManager根據不同持久層框架所對應的接口實現類,幾個比較常見的如下圖所示
比如我們在使用JDBC或者iBatis(就是Mybatis)進行數據持久化操作時,我們的xml配置通常如下:
<!-- 事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
</bean>
TransactionDefinition接口介紹
這個類就定義了事務的一些基本配置,描述了事務策略如何應用到方法上。事務屬性包含了5個方面:隔離級別,傳播行爲,回滾規則,是否只讀,事務超時。
接口內方法
public interface TransactionDefinition {
// 返回事務的傳播行爲
int getPropagationBehavior();
// 返回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些數據
int getIsolationLevel();
//返回事務的名字
String getName();
// 返回事務必須在多少秒內完成
int getTimeout();
// 返回是否優化爲只讀事務。
boolean isReadOnly();
}
事務隔離級別
定義了一個事務可能受到其他併發事務影響的程度
幾種併發導致的問題:
- 髒讀
- 丟失修改,A、B兩個事務讀取x,並做x–操作,當A先讀取到後進行x–,B緊接着也讀取到了同一個x值,並做x–,A的修改就被丟失了
- 不可重複讀
- 幻讀
不可重複讀與幻讀的區別
二者是類似的,區別在於不可重複讀重點在於修改,幻讀在於增刪。
幾種隔離級別
-
TransactionDefinition.ISOLATION_DEFAULT: 使用後端數據庫默認的隔離級別
-
TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 未提交讀
-
TransactionDefinition.ISOLATION_READ_COMMITTED: 提交讀
-
TransactionDefinition.ISOLATION_REPEATABLE_READ: 重複讀
-
TransactionDefinition.ISOLATION_SERIALIZABLE: 串行化
事務傳播行爲
爲了解決業務層方法之間相互調用的事務問題。
當事務方法被另一個事務方法(確定另一個也必須是事務方法??)調用的時候,必須指定事物應該如何進行傳播。
因爲方法可能繼續在現有事務中運行,也可能開啓一個新的事務。
Spring定義了幾個表示傳播行爲的常量,下面按照是否支持當前事務進行分類:
支持當前事務:
- TransactionDefinition.PROPAGATION_REQUIRED: 如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_MANDATORY: 如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。(mandatory:強制性)
不支持當前事務的情況:
-
TransactionDefinition.PROPAGATION_REQUIRES_NEW: 創建一個新的事務,如果當前存在事務,則把當前事務掛起。
-
TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務方式運行,如果當前存在事務,則把當前事務掛起。
-
TransactionDefinition.PROPAGATION_NEVER: 以非事務方式運行,如果當前存在事務,則拋出異常。
其他情況:
- TransactionDefinition.PROPAGATION_NESTED: 如果當前存在事務,則創建一個事務作爲當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
以 PROPAGATION_NESTED 啓動的事務內嵌於外部事務中(如果存在外部事務的話),此時,內嵌事務並不是一個獨立的事務,它依賴於外部事務的存在,只有通過外部的事務提交,才能引起內部事務的提交,嵌套的子事務不能單獨提交。如果熟悉 JDBC 中的保存點(SavePoint)的概念,那嵌套事務就很容易理解了,其實嵌套的子事務就是保存點的一個應用,一個事務中可以包括多個保存點,每一個嵌套子事務。另外,外部事務的回滾也會導致嵌套子事務的回滾。
事務超時屬性
所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。
事務只讀屬性
對事務性資源進行只讀操作或者讀寫操作。
事務性資源就是指那些被事務管理的資源,比如數據源,JMS資源,以及自定義的事務性資源等。
如果能標記爲只讀的話,可以提高事務處理的性能。
回滾規則
定義了哪些異常會導致事務回滾而那些不會。
默認情況下,事務只有遇到運行期異常時纔會回滾,而在遇到檢查型異常時不會回滾。
你可以聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。同樣的,你還可以聲明事務遇到特定的異常不會滾,即使這些異常是運行期異常。
檢查型異常與非檢查型異常
繼承自Runtime Exception或 Error 的是非檢查型異常,而繼承自 Exception 的則是檢查型異常(當然,Runtime Exception 本身也是 Exception 的子類)。
對非檢查型類異常可以不用捕獲,而檢查型異常則必須用try語句塊進行處理或者把異常交給上級方法處理總之就是必須寫代碼處理它。
TransactionStatus接口介紹
TransactionStatus接口用來記錄事務的狀態,該接口定義了一組方法,用來獲取或判斷事務的相應狀態信息.
PlatformTransactionManager.getTransaction(…) 方法返回一個 TransactionStatus 對象。返回的TransactionStatus 對象可能代表一個新的或已經存在的事務(如果在當前調用堆棧有一個符合條件的事務)。
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事物
boolean hasSavepoint(); // 是否有恢復點
void setRollbackOnly(); // 設置爲只回滾
boolean isRollbackOnly(); // 是否爲只回滾
boolean isCompleted; // 是否已完成
}
Spring支持兩種方式的事務管理
- 編程式事務管理:通過底層API或者Transaction Template手動管理事務,實際應用中很少使用
- 使用xml或者annotation配置聲明式事務管理:代碼侵入性最小,實際是通過aop實現
實現聲明式事務的四種方式
- 基於TransactionInterceptor的聲明式事務:Spring 聲明式事務的基礎,通常也不建議使用這種方式,但是與前面一樣,瞭解這種方式對理解 Spring 聲明式事務有很大作用。
- 基於TransactionProxyFactoryBean的聲明式事務:第一種方式的改進版本,簡化了配置文件的書寫,Spring2.0中已經不推薦了。
- 基於 和命名空間的聲明式事務管理:目前推薦的方式,使用aop實現,充分利用切點表達式的強大支持,使得管理事務更加靈活。
- 基於@Transaction的全註解方式:開發人員只需在配置文件中加上一行啓用相關後處理Bean的配置,然後在需要實施事務管理的
基於轉賬業務的案例
我們在兩次轉賬之間添加一個錯誤語句(對應銀行斷電等意外情況),如果這個時候兩次轉賬不能成功,則說明事務配置正確,否則,事務配置不正確。
編程式事務管理
底層API實現
xml配置
<bean class="org.springframework.transaction.support.DefaultTransactionDefinition" id="transactionDefinition">
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
java配置
@Autowired
TransactionDefinition transactionDefinition;
@Autowired
PlatformTransactionManager transactionManager;
...
public void transfer() {
TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
try {
//本地事務執行邏輯...
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
e.printStackTrace();
}
}
TransactionTemplate實現
xml配置
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="isolationLevelName" value="ISOLATION_DEFAULT"/>
</bean>
java
@Autowired
TransactionTemplate transactionTemplate;
public void transfer() {
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
Object result = null;
try {
//本地事務邏輯執行...
//如果順利執行,參數transactionStatus會自動提交
} catch (Exception e) {
transactionStatus.setRollbackOnly();
result = false;
e.printStackTrace();
}
return result;
}
});
}
順便說一下,TransactionTemplate是繼承於DefaultTransactionDefinition的。
AbstractPlatformTransactionManager中用到了很多ThreadLocal類變量,有空需要研究一下。
聲明式事務管理
基於AspectJ(aop)的
xml配置
<!--配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事務增強 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 做事務操作 -->
<tx:attributes>
<!-- 設置進行事務操作的方法匹配規則 -->
<!--
propagation:事務傳播行爲;
isolation:事務隔離級別;
read-only:是否只讀;
rollback-for:發生那些異常時回滾
timeout:事務過期時間
-->
<tx:method name="bankdemo*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 第三步:配置切面 切面即把增強用在方法的過程 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.kip.service.impl..*.*(..))"/>
</aop:config>
java
public void accountMoney () {
//正常業務邏輯執行...
}
基於註解的方式
xml配置
<!--配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 第二步: 開啓事務註解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 第三步 在方法所在類上加註解 -->
java
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false, timeout = -1)
public void bankdemo() {
//正常業務邏輯執行...
}