Spring持久化支援(一)-Spring事務管理
事務管理的目的是保證資料操作的事務性(原子性、一致性、隔離性、永續性ACID),脫離了事務性,DAO照樣可以順利地進行資料操作。
事務是一系列的動作, 它們被當做一個單獨的工作單元. 這些動作要麼全部完成, 要麼全部不起作用
Spring事務管理是Spring AOP技術的應用。
Spring對事務管理的支援
Spring為事務管理提供了一致的程式設計模板,在高層次建立了統一的事務抽象。
不管選擇Spring JDBC、Hibernate、JPA還是MyBatis,Spring都可讓使用者用統一的程式設計模型進行事務管理。
Spring為事務管理提供了事務模板類TransactionTemplate。
通過TransactionTemplate並配合使用事務回撥TransactionCallback指定具體的持久化操作,就可通過程式設計方式實現事務管理,無須關注資源獲取、複用、釋放、事務同步和異常處理等操作
事務管理作為一個切面織入目標業務方法的周圍,業務方法完全從事務程式碼中解脫出來,程式碼的複雜度大大降低。
被織入的事務程式碼基於Spring事務同步管理器進行工作,事務同步管理器維護著業務類物件執行緒相關的資源。DAO類需要利用資源獲取工具訪問底層資料連線,或者直接建立在相應的持久化模板類的基礎上。
要了解它們的內部機制,就必須事先了解ThreadLocal的工作機制。
Spring的事務配置主要提供兩方面的資訊:
其一,切點資訊,用於定位實施事務切面的業務類方法;
其二,控制事務行為的事務屬性,這些屬性包括事務隔離級別、事務傳播行為、超時時間、回滾規則等。
Spring通過aop/tx Schema名稱空間和@Transactional註解技術,大大簡化了宣告式事務配置的難度。同時,瞭解基於TransactionProxyFactoryBean代理類定義宣告式事務的工作機制,有助於理解事務增強的內部過程。
Spring事務管理:(優點)Spring-tx
提供統一的API介面支援不同的資源:具體的事務源不一樣也沒問題
提供宣告式事務管理:事務程式碼不需要嵌入程式碼,AOP
方便與Spring框架繼承
多個資源(資料來源)的事務管理、同步
Spring事務管理的亮點在於宣告式事務管理。
Spring允許通過宣告方式,在IoC配置中指定事務的邊界和事務屬性,Spring自動在指定的事務邊界上應用事務屬性。宣告式事務是EJB烜赫一時的技術,Spring讓這種技術平民化,甚至可以說,Spring的宣告事務比EJB的更為強大
核心介面
Spring事務管理SPI (Service Provider Interface)的抽象層主要包括3個介面,分別是 PlatformTransactionManager、TransactionDefinition 和 TransactionStatus,位於org.springframework.transaction包中
Spring事務管理涉及的介面的聯絡如下:
根據 TransactionDefinition 提供的事務屬性配置資訊建立事務,並用TransactionStatus描述這個啟用事務的狀態
PlatformTransactionManager通過TransactionDefinition設定事務相關資訊管理事務。
管理事務過程中,產生一些事務狀態:狀態由TransactionStatus
PlatformTransactionManager:事務管理介面
org.springframework.transaction.PlatformTransactionManager介面是Spring核心事務管理器。
事務的開啟、提交、回滾等由具體的事務管理器實現
事務只能被提交或回滾(或回滾到某個儲存點後提交)
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; //根據事務定義資訊從事務環境中返回一個己存在的事務,或者建立一個新的事務,並用TransactionStatus描述這個事務的狀態
void commit(TransactionStatus status) throws TransactionException; //:根據事務的狀態提交事務。如果事務狀態已經被標識為rollback-only,則該方法將執行一個回滾事務的操作。
void rollback(TransactionStatus status) throws TransactionException; //將事務回滾。當 commit()方法丟擲異常時,rollback()方法會被隱式呼叫。
}
Spring不直接管理事務,而是提供了多種事務管理器,將事務管理的職責委託給底層具體的持久化實現框架來完成
Spring事務管理的優點:為不同的事務API提供一致的程式設計模型,如JTA、JDBC、Hibernate、JPA。
Spring為不同的持久化框架提供了不同的PlatformTransactionManager介面實現。
PlatformTransactionManager實現類 |
說明 |
org.springframework.jdbc.datasource. DataSourceTransactionManager |
使用spring JDBC或mybatis進行持久化資料時使用 |
org.springframework.orm.hibernate5. HibernateTransactionManager |
使用Hibernate5版本進行持久化資料 |
org.springframework.orm.jpa.JpaTransactionManager |
Jpa時使用 |
org.springframework.orm.jdo.JdoTransactionManager |
Jdo時使用 |
org.springframework.transaction.jta. JtaTransactionManager |
使用JTA實現來管理事務,在一個事務跨越多個資源時必須使用 |
事務管理器以普通的 Bean 形式宣告在 Spring IOC 容器中
實現事務管理,首先要在Spring中配置好相應的事務管理器,為事務管理器指定資料資源及一些其他事務管理控制屬性
PlatformTransactionManager提供了統一的API
AbstractPlatformTransactionManager抽象類原始碼:
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager,Serializable {
@Override public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { Object transaction = doGetTransaction(); // 子類物件不同,所以用Object接受。歸子類去實現
// Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); if (definition == null) { // Use defaults if no transaction definition given. definition = new DefaultTransactionDefinition(); } if (isExistingTransaction(transaction)) { // 是否是已存在的事務
// Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } // Check definition settings for new transaction. if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { // 是否超時 throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // No existing transaction found -> check propagation behavior to find out how to proceed. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources = suspend(null); try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException ex) { resume(null, suspendedResources); throw ex; } catch (Error err) { resume(null, suspendedResources); throw err; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } }
// 子類實現的模板方法
protected abstract Object doGetTransaction() throws TransactionException;
protected boolean isExistingTransaction(Object transaction) throws TransactionException {
return false;
}
private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException {
// 判斷事務傳播屬性,不同的傳播屬性不同的行為
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
} catch (RuntimeException beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
} catch (Error beginErr) {
resumeAfterBeginException(transaction, suspendedResources, beginErr);
throw beginErr;
}
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - specify 'nestedTransactionAllowed' property with value 'true'");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
} else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// 判斷事務隔離級別 // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " +(currentIsolationLevel != null ?isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :"(unknown)"));
}
}
if (!definition.isReadOnly()) { // 是否只讀
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
protected final DefaultTransactionStatus prepareTransactionStatus( TransactionDefinition definition, Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources) {
DefaultTransactionStatus status = newTransactionStatus(definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
prepareSynchronization(status, definition);
return status;
}
/**
* Create a TransactionStatus instance for the given arguments.
*/
protected DefaultTransactionStatus newTransactionStatus( TransactionDefinition definition, Object transaction,boolean newTransaction,boolean newSynchronization,boolean debug, Object suspendedResources) {
boolean actualNewSynchronization = newSynchronization &&
!TransactionSynchronizationManager.isSynchronizationActive();
return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,
definition.isReadOnly(), debug, suspendedResources);
}
/*** Initialize transaction synchronization as appropriate. */
protected void prepareSynchronization(DefaultTransactionStatus status,TransactionDefinition definition) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
1)DataSourceTransactionManager - JDBC事務
DataSourceTransactionManager是通過呼叫java.sql.Connection來管理事務,而Connection是通過DataSource獲取到的。通過呼叫連線的commit()方法來提交事務,同樣,事務失敗則通過呼叫rollback()方法進行回滾。
如果使用Spring JDBC或MyBatis,由於它們都基於資料來源的Connection訪問資料庫, 只要在Spring中配置DataSourceTransactionManager就可以。
XML配置:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
或
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p: dataSource-ref=“dataSource”/>
DataSourceTransactionManager原始碼:
doGetTransaction()//從ThreadLocal中獲取一個Connection(相互對立的)
doBegin() //開啟事務
// 執行業務邏輯
// 根據業務邏輯的執行結果判斷是否要提交還是回滾
doCommit()//提交
doRallback() // 回滾
2)HibernateTransactionManager - Hibernate事務
HibernateTransactionManager實現細節:將事務管理的職責委託給org.hibernate.Transaction物件,Transaction是從Hibernate Session中獲取到的。當事務成功完成時,HibernateTransactionManager會呼叫Transaction物件的commit(),反之,會呼叫rollback()。
Hibernate使用org.hibemate.Session封裝Connection,所以要一個能夠建立Session的SessionFactory
在配置事務管理器時,需要引入sessionFactory屬性。
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
3)JpaTransactionManager - Java持久化API事務(JPA)
JpaTransactionManager只需要裝配一個JPA實體管理工廠(javax.persistence.EntityManagerFactory介面的任意實現)。JpaTransactionManager將與由工廠所產生的JPA EntityManager合作來構建事務。
(JPA 通過 javax.persistence.EntityTransaction 管理 JPA 的事務,EntityTransaction 物件可通過 javax.persistence.EntityManager#getTransaction()方法獲得,而 EntityManager 又通過一個工廠類方法 javax.persistence.EntityManagerFactory#createEntityManager()獲取。
在底層,JPA依然通過JDBC的Connection的事務方法完成最終的控制。因此,要配置一個JPA事務管理器,必須先提供一個DataSource,然後配置一個EntityManagerFactory,最後才配置JpaTransactionManager)
Java持久化API是Java持久化標準。如果用JPA則需用JpaTransactionManager來處理事務。
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
或
<bean id="entityManagerFactory" class=norg.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource_ref="dataSource”/>
</bean>
<bean id="transactionManager" class=" org.springframework.orm.jpa.JpaTransactionManager "
p:entityManagerFactory-ref="entityManagerFactory"/>
4)JtaTransactionManager - Java原生API事務
JtaTransactionManager將事務管理的責任委託給javax.transaction.UserTransaction和javax.transaction.TransactionManager物件,事務成功完成通過UserTransaction.commit()方法提交,事務失敗通過UserTransaction.rollback()方法回滾。
如果用其他持久化技術,或跨越多個事務管理源(如兩或是多個不同的資料來源),需要使用JtaTransactionManager:
JtaTransactionManager不需要知道DataSource和其他特定的資源,因為它引用容器提供的全域性事務管理
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:/TransactionManager" />
</bean>
5)JmsTransactionManager – 使用各種訊息中介軟體
事務管理器介面PlatformTransactionManager通過getTransaction(TransactionDefinition definition)得到事務, TransactionDefinition類定義了一些基本的事務屬性。
TransactionDefinition:事務的定義
描述事務的隔離級別、超時時間、是否為只讀事務和事務傳播規則等控制事務具體行為的事務屬性,這些事務屬性可通過XML配置或註解描述提供,也可通過手工程式設計的方式設定。
事務屬性是事務的一些基本配置,描述了事務策略如何應用到方法上。事務屬性包含了5個方面
public interface TransactionDefinition {
// 傳播行為
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
// 事務隔離級別(Spring提供多一個Default)
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
// 超時
int TIMEOUT_DEFAULT = -1;
int getPropagationBehavior(); // 返回事務的傳播行為
int getIsolationLevel(); // 返回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些資料
int getTimeout(); // 返回事務必須在多少秒內完成
boolean isReadOnly(); // 事務是否只讀,事務管理器能夠根據這個返回值進行優化,確保事務是隻讀的
String getName();
}
1)事務傳播行為(規則):Spring定義了7種傳播屬性
傳播行為:當事務方法被另一個事務方法呼叫時,必須指定事務應該如何傳播
如:方法可能繼續在現有事務中執行,也可能開啟一個新事務,並在自己的事務中執行
規定了事務方法和事務方法發生巢狀呼叫時事務如何進行傳播、
1.PROPAGATION_REQUIRED(常見的方式,預設)
當前方法必須執行在事務中。如果當前事務存在,方法將會在該事務中執行。沒有事務則新建一個事務
如果有事務在執行,當前的方法就在這個事務內執行,否則,新建一個事務,並在自己的事務內執行,。
2.PROPAGATION_SUPPORTS
支援當前事務。如果當前沒有事務,則以非事務方式執行
如果有事務在執行,當前的方法就在這個事務內執行,否則它不可以執行在事務中
3.PROPAGATION_MANDATORY
該方法必須在事務中執行,如果當前事務不存在,則會丟擲一個異常
當前的方法必須執行在事務內部,如果沒有正在執行的事務,就丟擲異常
4.PROPAGATION_REQUIRES_NEW
新建事務。如果當前存在事務,則把當前事務掛起。表示當前方法必須執行在它自己的事務中。一個新的事務將被啟動。如果存在當前事務,在該方法執行期間,當前事務會被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager
表示該方法必須啟動一個新事務, 並在自己的事務內執行. 如果有事務在執行, 就應該先掛起它
當前的方法必須啟動新事務,並在它自己的事務內執行,如果有事務正在執行,應該將它掛起
5.PROPAGATION_NOT_SUPPORTED
以非事務方式執行操作。如果當前存在事務,則把當前事務掛起。表示該方法不應該執行在事務中。如果存在當前事務,在該方法執行期間,當前事務將被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager
當前的方法不應該執行在事務內部,如果有執行的事務,則將它掛起
6.PROPAGATION_NEVER
以非事務方式執行。如果當前存在事務在執行,則丟擲異常
當前的反方不應該執行在事務中,如果有執行的事務,則丟擲異常
7.PROPAGATION_NESTED
如果當前存在事務,則在巢狀事務內執行:如果當前沒有事務,則執行與
PROPAGATION_REQUIRED 類似的操作。巢狀的事務可以獨立於當前事務進行單獨地提交或回滾。如果當前事務不存在,那麼其行為與PROPAGATION_REQUIRED一樣。注意各廠商對這種傳播行為的支援是有所差異的。可以參考資源管理器的文件來確認它們是否支援巢狀事務
如果有事務在執行,當前的方法就應該在這個事務的巢狀事務內執行,否則就啟動一個新的事務,並在他自己的事務內執行
注:在使用PROPAGATION_NESTED時,底層的資料來源必須基於JDBC3.0,並且實現者需要支援儲存點事務機制。
2)事務隔離級別
併發若無好的隔離級別,將導致髒讀、不可重複讀、幻讀等問題
Spring定義的隔離級別和java.sql.Connection介面的四個隔離級別同名。
1. ISOLATION_READ_UNCOMMITTED
2. ISOLATION_READ_COMMITTED
3. ISOLATION_REPEATABLE_READ
4. ISOLATION_SERIALIZABLE
5. ISOLATION_DEFAULT
表示使用底層資料庫的預設隔離級別
由於事務可以在行和表上獲得鎖, 因此長事務會佔用資源, 並對整體效能產生影響.
如果一個事物只讀取資料但不做修改, 資料庫引擎可以對這個事務進行優化.
3)事務超時
事務在超時前能執行多久,超過時間後,事務被回滾。有些事務管理器不支援事務過期的功能,這時,如果設定TIMEOUT_DEFAULT之外的其他值,則將丟擲異常
超時事務屬性: 事務在強制回滾之前可以保持多久. 這樣可以防止長期執行的事務佔用資源.
4)只讀
只讀事務不修改任何資料,資源事務管理者可以針對可讀事務應用一些優化措施,提高執行效能。只讀事務在某些情況下(如使用Hibernate時)是一種非常有用的優化,試圖在只讀事務中更改資料將引發異常。
只讀事務屬性: 表示這個事務只讀取資料但不更新資料, 這樣可以幫助資料庫引擎優化事務
5)回滾事務屬性
預設情況下只有未檢查異常(RuntimeException和Error型別的異常)會導致事務回滾. 而受檢查異常不會.
事務的回滾規則可以通過 @Transactional 註解的 rollbackFor 和 noRollbackFor 屬性來定義. 這兩個屬性被宣告為 Class[] 型別的, 因此可以為這兩個屬性指定多個異常類.
屬性配置與使用:
註解@Transaction:
@Transactional相關屬性:
Propagation:定義事務傳播屬性 org.springframework.transaction.annotation.Propagation
Isolation:設定隔離級別 org.springframework.transaction.annotation.Isolation
readOnly:設定只讀屬性 @Transactional(readOnly=true)
timeout:設定超時時間。單位為秒 @Transactional(timeout=10)
rollbackFor:設定異常回滾策略 @Transactional(rollbackFor={SQLException.class})) 多個異常之間可用逗號分隔
noRollbackFor:設定異常不回滾策略
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED, //事務隔離級別。讀與提交
readOnly=false, //只讀
//rollbackFor = ,
//noRollbackFor = {UserAccountException.class},//對於這種異常不回滾
timeout=3) //超時就回滾
@Transactional在類上面註解:適用於所有public的方法
@Transactional在方法上註解:將覆蓋類級別註解
使用不同的事務管理器:
Xml中定義不同的事務管理器:
每個事務管理器可以繫結一個獨立的資料來源。
Xml配置檔案配置:
在 Spring 2.x 事務通知中,在 <tx:method> 元素中設定傳播事務屬性、隔離級別、回滾策略(不止一種異常則用逗號隔開)、只讀屬性和超時時間。
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 根據方法名指定事務的屬性 -->
<tx:method name="purchase"
propagation="REQUIRES_NEW"
isolation="READ_COMMITTED"
rollback-for="java.io.IOException,java.sql.SQLException"
no-rollback-for="java.lang.ArithmeticException"
timeout="30"
read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
TransactionStatus:代表一個事務的具體執行狀態
事務管理器可通過該介面獲取事務執行期的狀態資訊,也可通過該介面間接回滾事務,它相比於在丟擲異常時回滾事務的方式更具可控性。
呼叫PlatformTransactionManager的getTransaction()的方法得到的是TransactionStatus介面的一個實現
TransactionStatus 擴充套件了 SavepointManager 介面。
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction();//當前事務是否是一個新的事務,當前事務是已經存在的事務返回false,或當前操作未執行在事務環境中
boolean hasSavepoint(); //判斷當前事務是否在內部建立了一個儲存點,該儲存點是為了支援Spring的巢狀事務而建立的
void setRollbackOnly();//將當前事務設定為rollback-only。通過該標識通知事務管理器只能將事務回滾,事務管理器將通過顯式呼叫回滾命令或丟擲異常的方式回滾事務
boolean isRollbackOnly();//判斷當前事務是否己經被標識為rollback-only只回滾
@Override
void flush();
boolean isCompleted();//判斷當前事務是否己經結束(己經提交或回滾)
}
該介面繼承於SavepointManager介面
SavepointManager介面基於JDBC 3.0儲存點的分段事務控制能力提供了巢狀事務的機制。
public interface SavepointManager {
Object createSavepoint() throws TransactionException; //建立一個儲存點物件,以便在後面可以利用
void rollbackToSavepoint(Object savepoint) throws TransactionException; //使事務回滾到特定的儲存點上,被回滾的儲存點將自動釋放,也可通過releaseSavepoint()釋放一個己經不用的儲存點
void releaseSavepoint(Object savepoint) throws TransactionException; //釋放一個儲存點。如果事務提交,則所有的儲存點會被自動釋放,無須手工清除
}
這3個方法在底層的資源不支援儲存點時,都將丟擲NestedTransactionNotSupportedException 異常
事務同步管理器:
Spring將JDBC的Connection、Hibernate的Session等訪問資料庫的連線或會話物件統稱為資源,這些資源在同一時刻是不能多執行緒共享的。為了讓DAO、Service類可能做到 singleton,
Spring事務同步管理器類org.springframework.transaction.support.TransactionSynchronizationManager使用ThreadLocal為不同事務執行緒提供了獨立的資源副本,同時維護事務配置的屬性和執行狀態資訊。
事務同步管理器是Spring事務管理的基石,不管使用者用的是程式設計式事務管理,還是宣告式事務管理,都離不開事務同步管理器。
Spring框架為不同的持久化技術提供了一套從TransactionSynchronizationManager中獲取對應執行緒繫結資源的工具類
在呼叫一個需要事務的元件的時候,管理器首先判斷當前呼叫(即當前執行緒)有沒有一個事務,如果沒有事務則啟動一個事務,並把事務與當前執行緒繫結。Spring使用TransactionSynchronizationManager的bindResource方法將當前執行緒與一個事務繫結,採用的方式就是ThreadLocal
public abstract class TransactionSynchronizationManager {
……
private static final ThreadLocal currentTransactionName = new ThreadLocal();
private static final ThreadLocal currentTransactionReadOnly = new ThreadLocal();
private static final ThreadLocal actualTransactionActive = new ThreadLocal(); ……
}
持久化技術 |
執行緒繫結資源獲取工具 |
Spring JDBC 或 MyBatis |
org. springframework .j dbc. datasource. DataSourceU tils |
Hibernate X.0 |
org.springframework.orm.hibemateX.SessionFactoryUtils |
JPA |
org.springframework.orm.jpa.EntityManagerFactoryUtils |
JDO |
org.springframework.orm.jdo.PersistenceManagerFactoryUtils |
這些工具類都提供了靜態的方法,通過這些方法可以獲取和當前執行緒繫結的資源,
如
DataSourceUtils.getConnection(DataSource dataSource)可從指定的資料來源中獲取和當前執行緒繫結的 Connection,
Hibernate 的 SessionFactoryUtils.getSession(SessionFactory sessionFactory,boolean allowCreate)可從指定的SessionFactory中獲取和當前執行緒繫結的Session
當需要脫離模板類,手工操作底層持久化技術的原生API時,就需要通過這些工具類獲取執行緒繫結的資源,而不應該直接從DataSource或SessionFactory中獲取。因為後者不能獲得與本執行緒相關的資源,因此無法讓資料操作參與到與本執行緒相關的事務環境中。
這些工具類還有另外一個重要的用途:將特定異常轉換為Spring的DAO異常
Spring為不同的持久化技術提供了模板類,模板類在內部通過資源獲取工具類間接訪問TransactionSynchronizationManager中的執行緒繫結資源。所以,如果DAO使用模板類進行持久化操作,這些DAO就可以配置成singleton。如果不使用模板類,也可以直接通過資源獲取工具類訪問執行緒相關的資源。
public abstract class TransactionSynchronizationManager {
//①用於儲存每個事務執行緒對應的Connection或Session等型別的資源
private static final ThreadLocal resources = new ThreadLocal();
//②用於儲存每個事務執行緒對應事務的名稱
private static final ThreadLocal currentTransactionName = new ThreadLocal();
//③用於儲存每個事務執行緒對應事務的read-only狀態
private static final ThreadLocal currentTransactionReadOnly = new ThreadLocal();
//④用於儲存每個事務執行緒對應事務的隔離級別
private static final ThreadLocal currentTransactionIsolationLevel= new ThreadLocal();
//⑤用於儲存每個事務執行緒對應事務的啟用態
private static final ThreadLocal actualTransactionActive = new ThreadLocal();
}
TransactionSynchronizationManager 將 DAO、Service 類中影響執行緒安全的所有“狀態”統一抽取到該類中,並用ThreadLocal進行替換,從此DAO (必須是基於模板類或資源獲取工具類建立的DAO)和Service (必須採用Spring事務管理機制)摘掉了非執行緒安全的帽子,完成了脫胎換骨式的身份轉變。
TransactionTemplate
執行緒安全,可在多個業務類中共享TransactionTemplate例項進行事務管理。
@SuppressWarnings("serial")
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean {
@Getter @Setter private PlatformTransactionManager transactionManager;
public TransactionTemplate() { }
public TransactionTemplate(