Spring 的事務管理
在一個實際專案中事務都是由業務層進行管理的,因為業務邏輯上的一組操作才是實際意義上的事務。

_Spring的事務管理.png
資料庫系統中有事務,Dao 層中也有事務,業務層中也有事務。三者範圍如圖所示

資料庫的事務
由具體的資料庫管理系統進行管理

_資料庫的事務.png
事務的ACID特性
-
原子性
:事務是一個不可分割的操作的集合 -
一致性
:當事務完成時,必須使所有的資料都保持一直狀態 -
隔離性
:事務和事務之間不能互相干擾。這裡的事務和執行緒就類似了,執行緒和執行緒之間也不應該互相干擾PROPAGATION_REQUIRED
-
永續性
:事務一旦提交,它在資料庫中的改變就是永久性的
不考慮事務隔離性帶來的問題
髒讀 不可重複讀 虛讀
事務的隔離級別
為了避免造成上述的幾種併發問題,在標準 SQL 規範中,定義了 4 個事務隔離級別,不同的隔離級別對事務的處理不同。
-
讀未提交
( Read Uncommitted ,1 級):什麼讀問題解決不了 -
已提交讀
( Read committed ,2 級):可以解決髒讀問題,必須在事務提交後,才可讀到資料 -
可重複讀
( Repeatable Read,4 級):解決髒讀和不可重複讀,也就是在另一個事務進行update
操作的時候,不能讀資料 -
序列化/序列化
( Serializable,8 級):完全符合事務的 ACID 的隔離級別,但是效率很低。
事務的隔離級別是由資料庫提供的保護機制,隔離級別越高,安全性就越高,但是效率就越低。
事務管理的API
Spring 中進行事務管理主要有三個介面
-
PlatformTransactionManager
,平臺事務管理器,是Spring用於管理事務的真正的物件。該介面有兩個實現類如下DataSourceTransactionManager HibernateTransactionManager
-
TransactionDefinition
,事務定義資訊,用於定義事務的相關的資訊,比如,隔離級別、超時資訊、傳播行為、是否只讀等 -
TransactionStatus
,事務的狀態,用於記錄在事務管理過程中,事務的狀態的物件。
事務管理的 API 的關係:Spring 進行事務管理的時候,首先 平臺事務管理器
根據 事務定義資訊
進行事務的管理,在事務管理過程中,產生各種狀態,將這些狀態的資訊記錄到 事務狀態
的物件中。
事務的傳播行為
業務層中的方法是一個事務,但是很多時候,業務層中的方法也需要互相呼叫,這就帶來了新的事務問題。
如何保證這種業務層方法互相呼叫產生的新的事務呢?這就是事務的傳播行為所需要關心的問題。
Spring 中提供了七種事務的傳播行為來解決此問題:
- 保證多個操作在同一個事務中
PROPAGATION_REQUIRED PROPAGATION_SUPPORTS PROPAGATION_MANDATORY
- 保證多個操作不在同一個事務中
PROPAGATION_REQUIRES_NEW PROPAGATION_NOT_SUPPORTED PROPAGATION_NEVER
- 巢狀式事務
-
PROPAGATION_NESTED
:巢狀事務,如果A中有事務,按照A的事務執行,執行完成後,設定一個儲存點,執行B中的操作,如果沒有異常,執行通過,如果有異常,可以選擇回滾到最初始位置,也可以回滾到儲存點。
-
確實有這麼多種型別的事務傳播行為,但是 Spring 中預設採用的是 PROPAGATION_REQUIRED
型別,總之它能實現的功能就是, 即使業務層中的某個方法使用了其他的方法,也能夠保證著不同的方法之間是一次原子操作。也就是用一個事務把這次操作涉及到的所有方法都當成是一個方法、一個事務看待。
宣告式事務
Spring 框架如何在我們編寫的程式碼的基礎上新增事務控制的程式碼呢?更準確的說是在業務層的方法之前開啟事務,在方法之後提交事務,並捕獲可能發生異常,隨時準備進行回滾操作呢?
這不就是典型的 AOP 問題嗎?直接在業務層的方法之前新增前置通知,之後新增後置通知不就是了嗎?Spring 的事務管理實現的底層機制就是 AOP 。如圖所示

_Spring事務實現機制.png
Spring 框架中對事務的封裝很完善,在實際配置中,如果沒有特殊要求的話,可以配置一個完全通用的模板,保證業務層所有操作都是一個事務,非常的方便,配置也很簡單。有以下幾個步驟即可
XML 配置
第一步,配置資料來源
<!-- 配置C3P0連線池--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
第二步,配置事務管理器實現類
<!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
第三步,配置事務的規則,這裡表示所有方法都採用預設的事務傳播管理機制,即保證所有操作都是事務安全的
<!-- 配置事務的增強 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 事務管理的規則 --> <tx:method name="*" propagation="REQUIRED" read-only="false"/> </tx:attributes> </tx:advice>
第四步,AOP 的配置,指定切入點和事務管理機制
<!-- aop的配置 --> <aop:config> <aop:pointcut expression="execution(* com.itheima.tx.demo2.AccountServiceImpl.*(..))" id="pointcut1"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config>
註解的配置
第一步,配置資料來源
<!-- 配置C3P0連線池--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
第二步,配置事務管理器實現類
<!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
第三步,開啟註解事務
<!-- 開啟註解事務 --> <tx:annotation-driven transaction-manager="transactionManager"/>
第四步,在業務層的類中新增一個 @Transactional
註解即可
事務理解很簡單,配置起來就更簡單了。