编程式事务管理
TODAY Framework 提供了两种编程式事务管理的方法,通过使用:
-
TransactionTemplate或TransactionalOperator。 -
直接使用
TransactionManager实现。
Infra 团队通常建议使用 TransactionTemplate 进行命令式流中的编程式事务管理,
并建议使用 TransactionalOperator 进行响应式代码。
第二种方法类似于使用 JTA UserTransaction API,尽管异常处理不那么繁琐。
使用 TransactionTemplate
TransactionTemplate 采用与其他 Infra 模板(如 JdbcTemplate)相同的方法。
它使用回调方法(使应用程序代码免于执行样板化的获取和释放事务资源),并产生意图驱动的代码,
即您的代码仅专注于您想要做的事情。
如下面的示例所示,使用 TransactionTemplate 绝对会将您耦合到 Infra 事务基础设施和 API。
编程式事务管理是否适合您的开发需求是您必须自己做出的决定。
|
必须在事务上下文中运行并显式使用 TransactionTemplate 的应用程序代码类似于下一个示例。
作为应用程序开发人员,您可以编写一个 TransactionCallback 实现(通常表示为匿名内部类),
其中包含您需要在事务上下文中运行的代码。然后,您可以将自定义 TransactionCallback 的实例传递给
TransactionTemplate 上公开的 execute(..) 方法。以下示例展示了如何执行此操作:
-
Java
public class SimpleService implements Service {
// 在此实例的所有方法之间共享的单个 TransactionTemplate
private final TransactionTemplate transactionTemplate;
// 使用构造函数注入来提供 PlatformTransactionManager
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod() {
return transactionTemplate.execute(new TransactionCallback() {
// 此方法中的代码在事务上下文中运行
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}
如果没有返回值,您可以使用方便的 TransactionCallbackWithoutResult 类和匿名类,如下所示:
-
Java
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});
回调中的代码可以通过在提供的 TransactionStatus 对象上调用 setRollbackOnly() 方法来回滚事务,如下所示:
-
Java
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessException ex) {
status.setRollbackOnly();
}
}
});
指定事务设置
您可以通过编程方式或在配置中指定 TransactionTemplate 上的事务设置(例如传播模式、隔离级别、超时等)。
默认情况下,TransactionTemplate 实例具有 默认事务设置。
以下示例显示了特定 TransactionTemplate 的事务设置的编程式自定义:
-
Java
public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
// 如果需要,可以在此处显式设置事务设置
this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
this.transactionTemplate.setTimeout(30); // 30 seconds
// 等等...
}
}
以下示例使用 Infra XML 配置定义具有一些自定义事务设置的 TransactionTemplate:
<bean id="sharedTransactionTemplate"
class="infra.transaction.support.TransactionTemplate">
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
<property name="timeout" value="30"/>
</bean>
然后,您可以将 sharedTransactionTemplate 注入到所需数量的服务中。
最后,TransactionTemplate 类的实例是线程安全的,因为实例不维护任何会话状态。
但是,TransactionTemplate 实例确实维护配置状态。因此,虽然许多类可以共享 TransactionTemplate 的单个实例,
但如果一个类需要使用具有不同设置(例如,不同的隔离级别)的 TransactionTemplate,
则需要创建两个不同的 TransactionTemplate 实例。
使用 TransactionalOperator
TransactionalOperator 遵循类似于其他响应式操作符的操作符设计。它使用回调方法
(使应用程序代码免于执行样板化的获取和释放事务资源),并产生意图驱动的代码,
即您的代码仅专注于您想要做的事情。
如下面的示例所示,使用 TransactionalOperator 绝对会将您耦合到 Infra 事务基础设施和 API。
编程式事务管理是否适合您的开发需求是您必须自己做出的决定。
|
必须在事务上下文中运行并显式使用 TransactionalOperator 的应用程序代码类似于下一个示例:
-
Java
public class SimpleService implements Service {
// 在此实例的所有方法之间共享的单个 TransactionalOperator
private final TransactionalOperator transactionalOperator;
// 使用构造函数注入来提供 ReactiveTransactionManager
public SimpleService(ReactiveTransactionManager transactionManager) {
this.transactionalOperator = TransactionalOperator.create(transactionManager);
}
public Mono<Object> someServiceMethod() {
// 此方法中的代码在事务上下文中运行
Mono<Object> update = updateOperation1();
return update.then(resultOfUpdateOperation2).as(transactionalOperator::transactional);
}
}
TransactionalOperator 可以通过两种方式使用:
-
使用 Project Reactor 类型的操作符风格(
mono.as(transactionalOperator::transactional)) -
适用于所有其他情况的回调风格(
transactionalOperator.execute(TransactionCallback<T>))
回调中的代码可以通过在提供的 ReactiveTransaction 对象上调用 setRollbackOnly() 方法来回滚事务,如下所示:
-
Java
transactionalOperator.execute(new TransactionCallback<>() {
public Mono<Object> doInTransaction(ReactiveTransaction status) {
return updateOperation1().then(updateOperation2)
.doOnError(SomeBusinessException.class, e -> status.setRollbackOnly());
}
}
});
取消信号
在 Reactive Streams 中,Subscriber 可以取消其 Subscription 并停止其 Publisher。
Project Reactor 以及其他库中的操作符,例如 next()、take(long)、timeout(Duration) 等,都可以发出取消信号。
无法知道取消的原因,是由于错误还是仅仅因为缺乏进一步消费的兴趣。
从 5.3 版本开始,取消信号会导致回滚。
因此,重要的是要考虑在事务 Publisher 下游使用的操作符。
特别是在 Flux 或其他多值 Publisher 的情况下,必须使用完整的输出以允许事务完成。
指定事务设置
您可以为 TransactionalOperator 指定事务设置(例如传播模式、隔离级别、超时等)。
默认情况下,TransactionalOperator 实例具有 默认事务设置。
以下示例显示了特定 TransactionalOperator 的事务设置的自定义:
-
Java
public class SimpleService implements Service {
private final TransactionalOperator transactionalOperator;
public SimpleService(ReactiveTransactionManager transactionManager) {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// 如果需要,可以在此处显式设置事务设置
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
definition.setTimeout(30); // 30 seconds
// 等等...
this.transactionalOperator = TransactionalOperator.create(transactionManager, definition);
}
}
使用 TransactionManager
以下部分解释了命令式和响应式事务管理器的编程式用法。
使用 PlatformTransactionManager
对于命令式事务,您可以直接使用 infra.transaction.PlatformTransactionManager 来管理您的事务。
为此,请通过 bean 引用将您使用的 PlatformTransactionManager 的实现传递给您的 bean。
然后,通过使用 TransactionDefinition 和 TransactionStatus 对象,您可以启动事务、回滚和提交。
以下示例展示了如何执行此操作:
-
Java
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 显式设置事务名称是只能通过编程方式完成的事情
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// 将您的业务逻辑放在这里
} catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
使用 ReactiveTransactionManager
在使用响应式事务时,您可以直接使用 infra.transaction.ReactiveTransactionManager 来管理您的事务。
为此,请通过 bean 引用将您使用的 ReactiveTransactionManager 的实现传递给您的 bean。
然后,通过使用 TransactionDefinition 和 ReactiveTransaction 对象,您可以启动事务、回滚和提交。
以下示例展示了如何执行此操作:
-
Java
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 显式设置事务名称是只能通过编程方式完成的事情
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
Mono<ReactiveTransaction> reactiveTx = txManager.getReactiveTransaction(def);
reactiveTx.flatMap(status -> {
Mono<Object> tx = ...; // 将您的业务逻辑放在这里
return tx.then(txManager.commit(status))
.onErrorResume(ex -> txManager.rollback(status).then(Mono.error(ex)));
});