跳到主要内容
版本:7.0.3

程序化事务管理

Hunyuan 7b 中英对照 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();
}
});
}
}

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

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

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

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

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

指定交易设置

你可以在 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...
}
}

以下示例通过使用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>

然后,你可以根据需要将 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);
}
}

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());
}
}
});

取消信号

在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);
}
}

使用 TransactionManager

以下部分将解释命令式(imperative)和反应式(reactive)事务管理器的编程使用方法。

使用 PlatformTransactionManager

对于命令型事务(imperative transactions),你可以直接使用 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);

使用 ReactiveTransactionManager

在处理反应式事务(reactive transactions)时,你可以直接使用 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)));
});