理解 Spring 框架事務抽象

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` 是一個介面,所以可以根據需要輕鬆地模擬或樁化它。它不與查詢策略(例如 JNDI)繫結。`PlatformTransactionManager` 實現像 Spring Framework IoC 容器中的任何其他物件(或 bean)一樣定義。僅此一項優勢就使 Spring Framework 事務成為一個值得使用的抽象,即使您使用 JTA 也是如此。您可以比直接使用 JTA 更容易地測試事務程式碼。

再次,為了與 Spring 的哲學保持一致,`PlatformTransactionManager` 介面的任何方法都可能丟擲的 `TransactionException` 是非檢查異常(也就是說,它擴充套件了 `java.lang.RuntimeException` 類)。事務基礎設施故障幾乎總是致命的。在應用程式程式碼實際上可以從事務故障中恢復的極少數情況下,應用程式開發人員仍然可以選擇捕獲和處理 `TransactionException`。關鍵是開發人員不是被迫這樣做。

`getTransaction(..)` 方法根據 `TransactionDefinition` 引數返回一個 `TransactionStatus` 物件。如果當前呼叫堆疊中存在匹配的事務,則返回的 `TransactionStatus` 可能表示新事務,也可能表示現有事務。後一種情況的含義是,與 Jakarta EE 事務上下文一樣,`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` 是一個介面,所以可以根據需要輕鬆地模擬或樁化它。

`TransactionDefinition` 介面指定

  • 傳播:通常,事務範圍內的所有程式碼都在該事務中執行。但是,如果事務方法在事務上下文已經存在時執行,您可以指定其行為。例如,程式碼可以繼續在現有事務中執行(常見情況),或者現有事務可以掛起並建立新事務。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,那麼您將使用透過 JNDI 獲取的容器 `DataSource`,並結合 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` 名稱空間中的 `` 標籤。有關更多資訊,請參閱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.jpa.hibernate.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.jpa.hibernate.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory"/>
</bean>

如果您使用 Hibernate 和 Jakarta EE 容器管理的 JTA 事務,您應該使用與前面 JDBC 的 JTA 示例中相同的 `JtaTransactionManager`,如以下示例所示。此外,建議透過其事務協調器以及可能還有其連線釋放模式配置來讓 Hibernate 瞭解 JTA

<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.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.jpa.hibernate.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"/>
© . This site is unofficial and not affiliated with VMware.