理解 TODAY Framework 事务抽象
Infra 事务抽象的关键是事务策略的概念。事务策略由 TransactionManager 定义,具体来说,用于命令式事务管理的 infra.transaction.PlatformTransactionManager 接口和用于响应式事务管理的 infra.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 实现像 TODAY Framework IoC 容器中的任何其他对象(或 bean)一样定义。即使在使用 JTA 时,仅此好处就使 TODAY Framework 事务成为一个值得的抽象。您可以比直接使用 JTA 更容易地测试事务代码。
同样,为了保持 Infra 哲学,由 PlatformTransactionManager 接口的任何方法抛出的 TransactionException 都是未检查的(即,它扩展了 java.lang.RuntimeException 类)。事务基础设施故障几乎总是致命的。在极少数情况下,应用程序代码实际上可以从事务故障中恢复,应用程序开发人员仍然可以选择捕获并处理 TransactionException。重点是开发人员并未_被迫_这样做。
getTransaction(..) 方法根据 TransactionDefinition 参数返回一个 TransactionStatus 对象。返回的 TransactionStatus 可能代表一个新事务,或者如果当前调用堆栈中存在匹配的事务,则可以代表一个现有事务。后一种情况的含义是,与 Jakarta EE 事务上下文一样,TransactionStatus 与执行线程相关联。
从 TODAY Framework 5.2 开始,Infra 还为使用响应式类型或 Kotlin 协程的响应式应用程序提供了事务管理抽象。以下清单显示了由 infra.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 接口指定:
-
传播:通常,事务范围内的所有代码都在该事务中运行。但是,您可以指定如果当事务上下文已存在时运行事务方法时的行为。例如,代码可以在现有事务中继续运行(常见情况),或者可以挂起现有事务并创建新事务。Infra 提供了 EJB CMT 中熟悉的所有事务传播选项。要阅读有关 Infra 中事务传播语义的信息,请参阅 事务传播。
-
隔离:此事务与其他事务的工作隔离的程度。例如,此事务可以看到其他事务未提交的写入吗?
-
超时:此事务在超时并由底层事务基础设施自动回滚之前运行多长时间。
-
只读状态:当您的代码读取但不修改数据时,您可以使用只读事务。只读事务在某些情况下(例如使用 Hibernate 时)可能是有用的优化。
这些设置反映了标准事务概念。如有必要,请参阅讨论事务隔离级别和其他核心事务概念的资源。理解这些概念对于使用 TODAY 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();
}
无论您在 Infra 中选择声明式还是编程式事务管理,定义正确的 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="infra.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
如果您在 Jakarta EE 容器中使用 JTA,则使用通过 JNDI 获取的容器 DataSource,并结合 Infra 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="infra.transaction.jta.JtaTransactionManager" />
<!-- 其他 <bean/> 定义在这里 -->
</beans>
JtaTransactionManager 不需要知道 DataSource(或任何其他特定资源),因为它使用容器的全局事务管理基础设施。
dataSource bean 的上述定义使用了 jee 命名空间中的 <jndi-lookup/> 标签。有关更多信息,请参阅 JEE 模式。
|
| 如果您使用 JTA,无论您使用什么数据访问技术,无论是 JDBC、Hibernate JPA 还是任何其他支持的技术,您的事务管理器定义都应该看起来相同。这是因为 JTA 事务是全局事务,可以征用任何事务资源。 |
在所有 Infra 事务设置中,应用程序代码无需更改。您只需更改配置即可更改事务的管理方式,即使该更改意味着从本地事务转移到全局事务,反之亦然。