跳到主要内容

编程式事务管理

ChatGPT-4o 中英对照 Programmatic Transaction Management

Spring 框架提供了两种编程式事务管理方式,使用:

  • TransactionTemplateTransactionalOperator

  • 直接使用 TransactionManager 实现。

Spring 团队通常推荐在命令式流程中使用 TransactionTemplate 进行编程式事务管理,而在响应式代码中使用 TransactionalOperator。第二种方法类似于使用 JTA 的 UserTransaction API,尽管异常处理不那么繁琐。

使用 TransactionTemplate

TransactionTemplate 采用与其他 Spring 模板(例如 JdbcTemplate)相同的方法。它使用回调方法(以免应用程序代码必须进行样板式的获取和释放事务性资源),并使代码具有意图导向性,因为你的代码只需专注于你想要做的事情。

备注

正如以下示例所示,使用 TransactionTemplate 确实会将你与 Spring 的事务基础设施和 API 紧密结合。程序化事务管理是否适合你的开发需求,这个决定需要你自己做出。

必须在事务上下文中运行并显式使用 TransactionTemplate 的应用程序代码类似于下一个示例。作为应用程序开发人员,您可以编写一个 TransactionCallback 实现(通常表示为匿名内部类),其中包含需要在事务上下文中运行的代码。然后,您可以将自定义 TransactionCallback 的实例传递给 TransactionTemplate 上公开的 execute(..) 方法。以下示例展示了如何实现:

public class SimpleService implements Service {

// single TransactionTemplate shared amongst all methods in this instance
private final TransactionTemplate transactionTemplate;

// use constructor-injection to supply the PlatformTransactionManager
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}

public Object someServiceMethod() {
return transactionTemplate.execute(new TransactionCallback() {
// the code in this method runs in a transactional context
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}
java

如果没有返回值,你可以使用方便的 TransactionCallbackWithoutResult 类与匿名类,如下所示:

transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});
java

回调中的代码可以通过调用提供的 TransactionStatus 对象上的 setRollbackOnly() 方法来回滚事务,如下所示:

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessException ex) {
status.setRollbackOnly();
}
}
});
java

指定事务设置

您可以在 TransactionTemplate 上以编程方式或在配置中指定事务设置(例如传播模式、隔离级别、超时等)。默认情况下,TransactionTemplate 实例具有默认事务设置。以下示例展示了针对特定 TransactionTemplate 的事务设置的编程自定义:

public class SimpleService implements Service {

private final TransactionTemplate transactionTemplate;

public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);

// the transaction settings can be set here explicitly if so desired
this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
this.transactionTemplate.setTimeout(30); // 30 seconds
// and so forth...
}
}
java

下面的示例通过使用 Spring XML 配置定义了一个具有一些自定义事务设置的 TransactionTemplate

<bean id="sharedTransactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
<property name="timeout" value="30"/>
</bean>
xml

然后,您可以将 sharedTransactionTemplate 注入到所需的多个服务中。

最后,TransactionTemplate 类的实例是线程安全的,因为实例不维护任何会话状态。然而,TransactionTemplate 实例确实维护配置状态。因此,虽然多个类可以共享一个 TransactionTemplate 实例,但如果一个类需要使用具有不同设置的 TransactionTemplate(例如,不同的隔离级别),则需要创建两个不同的 TransactionTemplate 实例。

使用 TransactionalOperator

TransactionalOperator 遵循与其他响应式操作符类似的操作符设计。它使用回调方法(以免应用程序代码需要进行样板式的获取和释放事务性资源),使得代码是意图驱动的,也就是说,你的代码只需专注于你想要做的事情。

备注

正如以下示例所示,使用 TransactionalOperator 确实会将你与 Spring 的事务基础设施和 API 紧密结合在一起。程序化事务管理是否适合你的开发需求是一个需要你自己做出的决定。

必须在事务上下文中运行并显式使用 TransactionalOperator 的应用程序代码类似于以下示例:

public class SimpleService implements Service {

// single TransactionalOperator shared amongst all methods in this instance
private final TransactionalOperator transactionalOperator;

// use constructor-injection to supply the ReactiveTransactionManager
public SimpleService(ReactiveTransactionManager transactionManager) {
this.transactionalOperator = TransactionalOperator.create(transactionManager);
}

public Mono<Object> someServiceMethod() {

// the code in this method runs in a transactional context

Mono<Object> update = updateOperation1();

return update.then(resultOfUpdateOperation2).as(transactionalOperator::transactional);
}
}
java

TransactionalOperator 可以通过两种方式使用:

  • 使用 Project Reactor 类型的操作符风格(mono.as(transactionalOperator::transactional)

  • 针对其他情况的回调风格(transactionalOperator.execute(TransactionCallback<T>)

回调中的代码可以通过在提供的 ReactiveTransaction 对象上调用 setRollbackOnly() 方法来回滚事务,如下所示:

transactionalOperator.execute(new TransactionCallback<>() {

public Mono<Object> doInTransaction(ReactiveTransaction status) {
return updateOperation1().then(updateOperation2)
.doOnError(SomeBusinessException.class, e -> status.setRollbackOnly());
}
}
});
java

取消信号

在 Reactive Streams 中,Subscriber 可以取消其 Subscription 并停止其 Publisher。Project Reactor 中的操作符,以及其他库中的操作符,如 next()take(long)timeout(Duration) 等,都可以发出取消信号。无法知道取消的原因是由于错误还是仅仅因为不再有兴趣继续消费。从 5.3 版本开始,取消信号会导致回滚。因此,考虑从事务 Publisher 下游使用的操作符是很重要的。特别是在 Flux 或其他多值 Publisher 的情况下,必须消费完整的输出以允许事务完成。

指定事务设置

您可以为 TransactionalOperator 指定事务设置(例如传播模式、隔离级别、超时等)。默认情况下,TransactionalOperator 实例具有默认事务设置。以下示例展示了如何为特定的 TransactionalOperator 自定义事务设置:

public class SimpleService implements Service {

private final TransactionalOperator transactionalOperator;

public SimpleService(ReactiveTransactionManager transactionManager) {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();

// the transaction settings can be set here explicitly if so desired
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
definition.setTimeout(30); // 30 seconds
// and so forth...

this.transactionalOperator = TransactionalOperator.create(transactionManager, definition);
}
}
java

使用 TransactionManager

以下部分解释了命令式和响应式事务管理器的编程使用。

使用 PlatformTransactionManager

对于命令式事务,您可以直接使用 org.springframework.transaction.PlatformTransactionManager 来管理您的事务。为此,通过 bean 引用将您使用的 PlatformTransactionManager 的实现传递给您的 bean。然后,通过使用 TransactionDefinitionTransactionStatus 对象,您可以启动事务、回滚和提交。以下示例展示了如何做到这一点:

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = txManager.getTransaction(def);
try {
// put your business logic here
} catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
java

使用 ReactiveTransactionManager

在处理响应式事务时,您可以直接使用 org.springframework.transaction.ReactiveTransactionManager 来管理事务。为此,通过 bean 引用将您使用的 ReactiveTransactionManager 的实现传递给您的 bean。然后,通过使用 TransactionDefinitionReactiveTransaction 对象,您可以启动事务、回滚和提交。以下示例展示了如何实现这一点:

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

Mono<ReactiveTransaction> reactiveTx = txManager.getReactiveTransaction(def);

reactiveTx.flatMap(status -> {

Mono<Object> tx = ...; // put your business logic here

return tx.then(txManager.commit(status))
.onErrorResume(ex -> txManager.rollback(status).then(Mono.error(ex)));
});
java