Spring Framework 交易抽象概念解析
Spring 交易抽象概念的關鍵在於交易策略的概念。交易策略由 TransactionManager
定義,特別是命令式交易管理的 org.springframework.transaction.PlatformTransactionManager
介面和反應式交易管理的 org.springframework.transaction.ReactiveTransactionManager
介面。以下列表顯示了 PlatformTransactionManager
API 的定義
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
這主要是一個服務提供者介面 (SPI),儘管您可以從您的應用程式碼以程式化方式使用它。由於 PlatformTransactionManager
是一個介面,因此可以根據需要輕鬆地進行模擬或 Stub。它不與查閱策略(例如 JNDI)綁定。PlatformTransactionManager
實作定義為 Spring Framework IoC 容器中的任何其他物件(或 Bean)。僅此優點就使 Spring Framework 交易成為值得的抽象概念,即使您使用 JTA 時也是如此。您可以比直接使用 JTA 更輕鬆地測試交易程式碼。
再次強調,與 Spring 的哲學保持一致,任何 PlatformTransactionManager
介面的方法都可能拋出的 TransactionException
是未檢查的(也就是說,它擴展了 java.lang.RuntimeException
類別)。交易基礎架構故障幾乎總是致命的。在極少數情況下,應用程式碼實際上可以從交易故障中恢復,應用程式開發人員仍然可以選擇捕獲和處理 TransactionException
。重點是開發人員並非被迫這樣做。
getTransaction(..)
方法根據 TransactionDefinition
參數傳回 TransactionStatus
物件。傳回的 TransactionStatus
可能代表新的交易,或者如果目前呼叫堆疊中存在相符的交易,則可以代表現有的交易。後一種情況的含義是,與 Jakarta EE 交易 Context 一樣,TransactionStatus
與執行緒相關聯。
Spring 也為使用反應式類型或 Kotlin 協程的反應式應用程式提供了交易管理抽象概念。以下列表顯示了 org.springframework.transaction.ReactiveTransactionManager
定義的交易策略
public interface ReactiveTransactionManager extends TransactionManager {
Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException;
Mono<Void> commit(ReactiveTransaction status) throws TransactionException;
Mono<Void> rollback(ReactiveTransaction status) throws TransactionException;
}
反應式交易管理器主要是一個服務提供者介面 (SPI),儘管您可以從您的應用程式碼以程式化方式使用它。由於 ReactiveTransactionManager
是一個介面,因此可以根據需要輕鬆地進行模擬或 Stub。
TransactionDefinition
介面指定
-
傳播:通常,交易範圍內的所有程式碼都在該交易中執行。但是,您可以指定在已存在交易 Context 時執行交易方法時的行為。例如,程式碼可以繼續在現有的交易中執行(常見情況),或者可以暫停現有的交易並建立新的交易。Spring 提供了 EJB CMT 中熟悉的所有交易傳播選項。若要閱讀 Spring 中交易傳播的語意,請參閱交易傳播。
-
隔離:此交易與其他交易的工作隔離的程度。例如,此交易是否可以看到來自其他交易的未提交寫入?
-
逾時:此交易在逾時並由底層交易基礎架構自動回滾之前執行的時間長度。
-
唯讀狀態:當您的程式碼讀取但不修改資料時,可以使用唯讀交易。在某些情況下,例如當您使用 Hibernate 時,唯讀交易可能是一種有用的最佳化。
這些設定反映了標準交易概念。如有必要,請參考討論交易隔離層級和其他核心交易概念的資源。理解這些概念對於使用 Spring Framework 或任何交易管理解決方案至關重要。
TransactionStatus
介面為交易程式碼提供了一種簡單的方式來控制交易執行和查詢交易狀態。這些概念應該很熟悉,因為它們在所有交易 API 中都很常見。以下列表顯示了 TransactionStatus
介面
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
@Override
boolean isNewTransaction();
boolean hasSavepoint();
@Override
void setRollbackOnly();
@Override
boolean isRollbackOnly();
void flush();
@Override
boolean isCompleted();
}
無論您在 Spring 中選擇宣告式或程式化交易管理,定義正確的 TransactionManager
實作都絕對至關重要。您通常透過相依性注入來定義此實作。
TransactionManager
實作通常需要了解其工作環境:JDBC、JTA、Hibernate 等。以下範例顯示了如何定義本機 PlatformTransactionManager
實作(在本例中,使用純 JDBC)。
您可以透過建立類似於以下的 Bean 來定義 JDBC DataSource
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
相關的 PlatformTransactionManager
Bean 定義隨後具有對 DataSource
定義的參考。它應該類似於以下範例
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
如果您在 Jakarta EE 容器中使用 JTA,則您可以使用容器 DataSource
,透過 JNDI 取得,並結合 Spring 的 JtaTransactionManager
。以下範例顯示了 JTA 和 JNDI 查閱版本的外觀
<?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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
https://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- other <bean/> definitions here -->
</beans>
JtaTransactionManager
不需要了解 DataSource
(或任何其他特定資源),因為它使用容器的全域交易管理基礎架構。
dataSource Bean 的上述定義使用了來自 jee Namespace 的 <jndi-lookup/> 標籤。如需更多資訊,請參閱JEE Schema。 |
如果您使用 JTA,則您的交易管理器定義應該看起來相同,無論您使用何種資料存取技術,是 JDBC、Hibernate JPA 還是任何其他支援的技術。這是因為 JTA 交易是全域交易,可以徵募任何交易資源。 |
在所有 Spring 交易設定中,應用程式碼都不需要變更。您只需變更配置即可變更交易的管理方式,即使該變更意味著從本機交易移動到全域交易或反之亦然。
Hibernate 交易設定
您也可以輕鬆使用 Hibernate 本機交易,如下列範例所示。在這種情況下,您需要定義 Hibernate LocalSessionFactoryBean
,您的應用程式碼可以使用它來取得 Hibernate Session
實例。
DataSource
Bean 定義與先前顯示的本機 JDBC 範例類似,因此未在以下範例中顯示。
如果 DataSource (由任何非 JTA 交易管理器使用)透過 JNDI 查閱並由 Jakarta EE 容器管理,則它應該是非交易性的,因為交易由 Spring Framework(而不是 Jakarta EE 容器)管理。 |
在本例中,txManager
Bean 的類型為 HibernateTransactionManager
類型。與 DataSourceTransactionManager
需要參考 DataSource
的方式相同,HibernateTransactionManager
需要參考 SessionFactory
。以下範例宣告了 sessionFactory
和 txManager
Bean
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
如果您使用 Hibernate 和 Jakarta EE 容器管理的 JTA 交易,則您應該使用與 JDBC 先前的 JTA 範例中相同的 JtaTransactionManager
,如下列範例所示。此外,建議透過其交易協調器以及可能的連線釋放模式配置來讓 Hibernate 了解 JTA
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.transaction.coordinator_class=jta
hibernate.connection.handling_mode=DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
或者,您可以將 JtaTransactionManager
傳遞到您的 LocalSessionFactoryBean
中,以強制執行相同的預設值
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
<property name="jtaTransactionManager" ref="txManager"/>
</bean>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>