Spring的事務管理
事務的基礎知識
什麼時事務
一榮俱榮,一損俱損非常形象的比如了事務,事務要麼整體成功,要麼整體失敗。 事務的ACID
- 原子性(ATOM)
- 一致性(consistency)
- 隔離性(Isolation)
- 永續性(Durabiliy)
資料併發問題
- 髒讀(dirty read) A事務讀取B事務尚未提交的更改資料,A事務讀取的資料不能保證持久。
- 不可重複讀(unrepeatable read) A事務讀取了B事務已經提交的更改資料。A事務應該讀取B事務之前的資料
- 幻象讀(phantom read) A事務讀取B事務提交的新增資料。
- 第一類丟失更新 A事務撤銷時把已經提交的B的事務的更新資料覆蓋了
- 第二類丟失更新 A事務覆蓋B事務已經提交的資料
幻象讀讀到了其他已經提交事務的新增資料,不可重複讀是指讀到了已經提交事務的更改資料(更改或者刪除) 不可重複讀只用採取新增行級鎖,阻止操作中的資料發生變化 幻象讀只能新增表級鎖防止新增資料。
資料庫的鎖機制
資料庫通過鎖機制解決併發訪問的問題。按照鎖定的物件的不同,分為表鎖定和行鎖定。 從併發事務鎖定的關係上看,分為共享鎖定和獨佔鎖定。共享鎖定會防止獨佔鎖定但執行其他的共享鎖定,獨佔鎖定防止其他獨佔鎖定和其他的共享鎖定。 INSERT,UPDATE,DELETE和SELECT FOR UPDATE採用行鎖定
事務隔離級別
資料庫為使用者提供了自動鎖機制,只要使用者指定會話的事務隔離級別,資料庫就會分析事務中的SQL語句,自動為事務操作的資料資源新增適合的鎖。 SQL92定義了4個級別的事務隔離
隔離級別 | 髒讀 | 不可重複讀 | 幻象讀 | 第一類丟失更新 | 第二類丟失更新 |
---|---|---|---|---|---|
READ UNCOMMITED | Y | Y | Y | N | Y |
READ COMMITED | N | Y | Y | N | N |
REPEATABLE READ | N | N | Y | N | N |
SERIALIZABLE | N | N | N | N | N |
JDBC對事務的支援
Connection conn = ds.getConnection();
System.out.println("supportsTransactions:"+conn.getMetaData().supportsTransactions());//顯式資料庫是否支援事務
System.out.println("supportsTransactionIsolationLevel1:"+conn.getMetaData().supportsTransactionIsolationLevel(1));//是否支援事務級別
複製程式碼
簡單的事務提交
Connection conn;
try{
conn = DriverManager.getConnection();
conn.setAutoCommit(false);//關閉自動提交
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
Statement stmt = conn.createStatement();
//....
conn.commit();
}catch(Exception e){
...//處理異常
conn.rollback();
}finally{
//
}
複製程式碼
Spring對事務管理的支援
Spring為事務管理提供了一致的程式設計模板,在高層次建立了統一的事務抽象。不管時JDBC,Hibernate,JPA等使用統一的程式設計模型進行事務管理。
事務管理關鍵抽象
Spring事務管理SPI的抽象層主要包括3個介面:PlatformTransactionManager
,TransactionDefinition
,TransactionStatus
.
TransactionDefinition用於事務隔離級別,超時時間,是否為只讀事務,事務傳播規則,通過xml配置或者註解,程式設計等方式提供。
PlatformTransactionManager根據TransactionDefinition提供的事務屬性配置資訊建立事務,並用TransactionStatus描述這個啟用事務的狀態。
- TransactionDefinition
- 事務隔離:4種事務隔離,常年在java.sql.Connection介面中,其中ISOLATION_DEFAULT表示預設級別,由資料庫直接定義。
- 事務傳播:通常在一個事務中執行的所有程式碼都會執行於同一個事務上下文中。Spring提供了幾個事務傳播型別
- 事務超時:事務在超時前能執行多久,超過時間後,事務被回滾。TIMEOUT_DEFAULT之外的其他值,則將丟擲異常。
- 只讀狀態:只讀事務不修改任何資料,當它修改資料後就丟擲異常。
- TransactionStatus
代表一個事務的具體執行狀態。事務管理器可以通過該介面獲取事務執行期間的狀態資訊。
boolean hasSavepoint()
判斷當前事務是否在內部建立一個儲存點。
boolean isNewTransaction()
判斷當前事務是否是一個新的事務,如果返回false,則表示當前事務是一個已經存在的事務,或者當前操作未執行在事務環境中。
boolean isCompleted()
判斷當前事務是否已經結束
boolean isRollbackOnly()
判斷當前事務是否已經被標識為rollback-only
- PlatformTransactionManager
getTransaction(TransactionDefinition definition)
返回事務定義對應的事務狀態,或者建立一個新的事務
commit(TransactionStatus status)
提交事務,如果status設定了rollback-only,就執行一個回滾事務的操作
rollback(TransactionSatatus status)
提交丟擲異常時rollback方法會被隱式呼叫。
事務同步管理器
SynchronizationManager
使用ThreadLocal為不同事務執行緒提供了獨立的資源副本,同事維護事務配置的屬性和執行狀態資訊。
事務傳播行為
當事務執行中出現多個呼叫服務,Spring通過過事務傳播行為控制當前的事務如果傳播到被巢狀呼叫的目標服務介面方法中。
Spring在TransactionDefinition介面定義了7種型別的事務傳播行為
事務傳播行為型別 | 說明 |
---|---|
PROPAGATION_REQUIRED | 沒有事務,新建一個事務,已經存在一個事務,則加入到事務中 |
PROPAGATION_SUPPORTS | 支援當前事務,沒有事務就以非事務執行 |
PROPAGATION_MANDATORY | 使用當前事務,如果沒有事務就拋異常 |
PROPAGATION_REQUIRES_NEW | 新建事務,如果已經存在事務則把當前事務掛起 |
PROPAGATION_NOT_SUPPORTED | 以非事務方式執行,如果當前存在事務,則掛起當前事務 |
PROPAGATION_NEVER | 以非事務方式執行,如果當前存在事務,則拋異常 |
PROPAGATION_NESTED | 如果當前存在事務,則在巢狀事務內執行,如果沒有當前事務,則執行PROPAGATION_REQUIRED類似操作 |
程式設計式的事務管理
很少需要通過程式設計來進行事務管理。Spring也提供了模板類滿足特殊場合的需要,org.springframework.transaction.support.TransactionTemplate
。它式執行緒安全的可以在多個業務類中共享TransactionTemplate例項進行事務管理。
TransactionTemplate tt = context.getBean("transactionTemplate", TransactionTemplate.class);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
JDBCDao dao = context.getBean("jdbcDao", JDBCDao.class);
try{
int i = dao.update("update product_info f set f.product_stock = 50"
+ " where f.product_stock = 67");
System.out.println("i>>>"+i);
}catch(Exception e){
e.printStackTrace();
status.setRollbackOnly();//呼叫status物件的setRollbackOnly()方法告知事務管理器當前事務需要回滾
}
}
})
複製程式碼
在回撥介面方法中需要顯式訪問底層資料連線,則必須通過資源獲取工具類得到執行緒繫結的資料連線。
使用XML配置宣告式事務
宣告式事務對程式碼的入侵性最小,可以讓事務管理程式碼完全從業務程式碼中移除。Spring中宣告事務通過SpringAOP實現的,Spring複製將事務管理增強邏輯動態織入業務方法的相應連線點中(執行緒繫結資源,開始事務,提交/回滾事務,進行異常轉換和處理)
宣告式事務:
- TransactionProxyFactoryBean(不推薦)
- 基於aop/tx名稱空間的配置(xml配置)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<aop:aspectj-autoproxy />
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* spring.jdbcdemo.learnjdbc.service.*.*(..))" />
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="update*" rollback-for="Exception" />
<tx:method name="test*" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
</beans>
複製程式碼
Spring的事務模組支援以aop切面的形式來呼叫事務的方法,提供了tx:advice配置 tx:advice元素有兩個屬性id和transaction-manager id是用在aop配置中,當呼叫方法時,aop會根據id獲取對應的aop配置 transaction-manager是事務管理器,在aop會從它那裡獲取事務
tx:method元素配置哪些方法配置需要由事務切面呼叫和該方法執行的事務的相關屬性
屬性 | 是否必需 | 預設值 | 描述 |
---|---|---|---|
name | 是 | 與事務屬性關聯的方法名,可以使用萬用字元 | get*,on*Event |
propagation | 否 | REQUIRED | 事務傳播 |
isolation | 否 | DEFAULT | 隔離級別 |
timeout | 否 | -1 | 事務超時的時間,-1由底層事務系統決定 |
read-only | 否 | false | 事務是否只讀 |
rollback-for | 否 | 執行異常回滾 | 觸發事務回滾的Exception,多個異常用逗號分隔 |
no-rollback-for | 否 | 所有檢查型異常不會滾 | 參考上一行 |
- 註解配置宣告式事務(常用)
@Transactional可以修飾類和方法上,如果兩者都修飾了,方法上的配置會覆蓋類上的配置。
開啟事務註解
<tx:annotaion-driven transaction-manager="txManager">
複製程式碼
註解屬性和xml配置類似,多了rollbackForClassName和noRollbackForClassName可以直接填寫異常類