理解 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.