Programmatic Transaction Management

Spring Framework 提供了两种通过以下方式进行编程事务管理的方法:

The Spring Framework provides two means of programmatic transaction management, by using:

  • The TransactionTemplate or TransactionalOperator.

  • A TransactionManager implementation directly.

Spring 团队通常建议将 TransactionTemplate 用于命令式流程中的编程事务管理,将 TransactionalOperator 用于反应式代码。第二种方法类似于使用 JTA UserTransaction API,尽管异常处理不太麻烦。

The Spring team generally recommends the TransactionTemplate for programmatic transaction management in imperative flows and TransactionalOperator for reactive code. The second approach is similar to using the JTA UserTransaction API, although exception handling is less cumbersome.

Using the TransactionTemplate

TransactionTemplate 采用与其他 Spring 模板相同的方法,例如 JdbcTemplate。它使用回调方法(使应用程序代码摆脱了执行样板获取和释放事务资源的麻烦),从而生成意图驱动的代码,即代码只关注想要做什么。

The TransactionTemplate adopts the same approach as other Spring templates, such as the JdbcTemplate. It uses a callback approach (to free application code from having to do the boilerplate acquisition and release transactional resources) and results in code that is intention driven, in that your code focuses solely on what you want to do.

如下例所示,使用 TransactionTemplate 绝对会将您绑定到 Spring 的事务基础设施和 API。项目管理事务是否适合您的开发需求,需要您自己做出决定。

As the examples that follow show, using the TransactionTemplate absolutely couples you to Spring’s transaction infrastructure and APIs. Whether or not programmatic transaction management is suitable for your development needs is a decision that you have to make yourself.

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

Application code that must run in a transactional context and that explicitly uses the TransactionTemplate resembles the next example. You, as an application developer, can write a TransactionCallback implementation (typically expressed as an anonymous inner class) that contains the code that you need to run in the context of a transaction. You can then pass an instance of your custom TransactionCallback to the execute(..) method exposed on the TransactionTemplate. The following example shows how to do so:

  • Java

  • Kotlin

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();
			}
		});
	}
}
// use constructor-injection to supply the PlatformTransactionManager
class SimpleService(transactionManager: PlatformTransactionManager) : Service {

	// single TransactionTemplate shared amongst all methods in this instance
	private val transactionTemplate = TransactionTemplate(transactionManager)

	fun someServiceMethod() = transactionTemplate.execute<Any?> {
		updateOperation1()
		resultOfUpdateOperation2()
	}
}

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

If there is no return value, you can use the convenient TransactionCallbackWithoutResult class with an anonymous class, as follows:

  • Java

  • Kotlin

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

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

Code within the callback can roll the transaction back by calling the setRollbackOnly() method on the supplied TransactionStatus object, as follows:

  • Java

  • Kotlin

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

	protected void doInTransactionWithoutResult(TransactionStatus status) {
		try {
			updateOperation1();
			updateOperation2();
		} catch (SomeBusinessException ex) {
			status.setRollbackOnly();
		}
	}
});
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {

	override fun doInTransactionWithoutResult(status: TransactionStatus) {
		try {
			updateOperation1()
			updateOperation2()
		} catch (ex: SomeBusinessException) {
			status.setRollbackOnly()
		}
	}
})

Specifying Transaction Settings

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

You can specify transaction settings (such as the propagation mode, the isolation level, the timeout, and so forth) on the TransactionTemplate either programmatically or in configuration. By default, TransactionTemplate instances have the default transactional settings. The following example shows the programmatic customization of the transactional settings for a specific TransactionTemplate:

  • Java

  • Kotlin

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...
	}
}
class SimpleService(transactionManager: PlatformTransactionManager) : Service {

	private val transactionTemplate = TransactionTemplate(transactionManager).apply {
		// the transaction settings can be set here explicitly if so desired
		isolationLevel = TransactionDefinition.ISOLATION_READ_UNCOMMITTED
		timeout = 30 // 30 seconds
		// and so forth...
	}
}

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

The following example defines a TransactionTemplate with some custom transactional settings by using Spring XML configuration:

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

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

You can then inject the sharedTransactionTemplate into as many services as are required.

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

Finally, instances of the TransactionTemplate class are thread-safe, in that instances do not maintain any conversational state. TransactionTemplate instances do, however, maintain configuration state. So, while a number of classes may share a single instance of a TransactionTemplate, if a class needs to use a TransactionTemplate with different settings (for example, a different isolation level), you need to create two distinct TransactionTemplate instances.

Using the TransactionalOperator

TransactionalOperator 遵循的操作员设计类似于其他反应式操作员。它使用回调方法(使应用程序代码摆脱了执行样板获取和释放事务资源的麻烦),从而生成意图驱动的代码,即代码只关注想要做什么。

The TransactionalOperator follows an operator design that is similar to other reactive operators. It uses a callback approach (to free application code from having to do the boilerplate acquisition and release transactional resources) and results in code that is intention driven, in that your code focuses solely on what you want to do.

如下例所示,使用 TransactionalOperator 绝对会将您绑定到 Spring 的事务基础设施和 API。项目管理事务是否适合您的开发需求,需要您自己做出决定。

As the examples that follow show, using the TransactionalOperator absolutely couples you to Spring’s transaction infrastructure and APIs. Whether or not programmatic transaction management is suitable for your development needs is a decision that you have to make yourself.

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

Application code that must run in a transactional context and that explicitly uses the TransactionalOperator resembles the next example:

  • Java

  • Kotlin

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);
	}
}
// use constructor-injection to supply the ReactiveTransactionManager
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {

	// single TransactionalOperator shared amongst all methods in this instance
	private val transactionalOperator = TransactionalOperator.create(transactionManager)

	suspend fun someServiceMethod() = transactionalOperator.executeAndAwait<Any?> {
		updateOperation1()
		resultOfUpdateOperation2()
	}
}

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

TransactionalOperator can be used in two ways:

  • Operator-style using Project Reactor types (mono.as(transactionalOperator::transactional))

  • Callback-style for every other case (transactionalOperator.execute(TransactionCallback<T>))

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

Code within the callback can roll the transaction back by calling the setRollbackOnly() method on the supplied ReactiveTransaction object, as follows:

  • Java

  • Kotlin

transactionalOperator.execute(new TransactionCallback<>() {

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

	override fun doInTransactionWithoutResult(status: ReactiveTransaction) {
		updateOperation1().then(updateOperation2)
					.doOnError(SomeBusinessException.class, e -> status.setRollbackOnly())
	}
})

Cancel Signals

在 Reactive Streams 中,Subscriber 可以取消其 Subscription 并停止其 Publisher。Project Reactor 中以及 next(),take(long), timeout(Duration) 这样的其他库中的操作员都可以发出取消。无法得知取消的原因,是由于错误还是仅仅是对进一步消费缺乏兴趣。从版本 5.3 开始,取消信号会导致回滚。因此,重要的是考虑从事务 Publisher 使用的下游操作员。特别是在 Flux 或其他多值 Publisher 的情况下,必须消耗全部输出才能使事务完成。

In Reactive Streams, a Subscriber can cancel its Subscription and stop its Publisher. Operators in Project Reactor, as well as in other libraries, such as next(), take(long), timeout(Duration), and others can issue cancellations. There is no way to know the reason for the cancellation, whether it is due to an error or a simply lack of interest to consume further. Since version 5.3 cancel signals lead to a roll back. As a result it is important to consider the operators used downstream from a transaction Publisher. In particular in the case of a Flux or other multi-value Publisher, the full output must be consumed to allow the transaction to complete.

Specifying Transaction Settings

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

You can specify transaction settings (such as the propagation mode, the isolation level, the timeout, and so forth) for the TransactionalOperator. By default, TransactionalOperator instances have default transactional settings. The following example shows customization of the transactional settings for a specific TransactionalOperator:

  • Java

  • Kotlin

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);
	}
}
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {

	private val definition = DefaultTransactionDefinition().apply {
		// the transaction settings can be set here explicitly if so desired
		isolationLevel = TransactionDefinition.ISOLATION_READ_UNCOMMITTED
		timeout = 30 // 30 seconds
		// and so forth...
	}
	private val transactionalOperator = TransactionalOperator(transactionManager, definition)
}

Using the TransactionManager

以下部分介绍了命令式和响应式事务管理器的编程用法。

The following sections explain programmatic usage of imperative and reactive transaction managers.

Using the PlatformTransactionManager

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

For imperative transactions, you can use a org.springframework.transaction.PlatformTransactionManager directly to manage your transaction. To do so, pass the implementation of the PlatformTransactionManager you use to your bean through a bean reference. Then, by using the TransactionDefinition and TransactionStatus objects, you can initiate transactions, roll back, and commit. The following example shows how to do so:

  • Java

  • Kotlin

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);
val def = DefaultTransactionDefinition()
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName")
def.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED

val status = txManager.getTransaction(def)
try {
	// put your business logic here
} catch (ex: MyException) {
	txManager.rollback(status)
	throw ex
}

txManager.commit(status)

Using the ReactiveTransactionManager

在使用响应式事务时,你可以直接使用 org.springframework.transaction.ReactiveTransactionManager 管理你的事务。要做到这一点,你需要通过 Bean 引用将你要使用的 ReactiveTransactionManager 的实现传递给你的 Bean。然后,通过使用 TransactionDefinitionReactiveTransaction 对象,你可以启动事务、回滚和提交。以下示例展示了如何执行此操作:

When working with reactive transactions, you can use a org.springframework.transaction.ReactiveTransactionManager directly to manage your transaction. To do so, pass the implementation of the ReactiveTransactionManager you use to your bean through a bean reference. Then, by using the TransactionDefinition and ReactiveTransaction objects, you can initiate transactions, roll back, and commit. The following example shows how to do so:

  • Java

  • Kotlin

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)));
});
val def = DefaultTransactionDefinition()
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName")
def.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED

val reactiveTx = txManager.getReactiveTransaction(def)
reactiveTx.flatMap { status ->

	val tx = ... // put your business logic here

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