对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

回滚声明式事务

上一节概述了如何在应用程序中以声明方式为类(通常是服务层类)指定事务设置的基础知识。本节介绍如何在 XML 配置中以简单、声明式的方式控制事务的回滚。有关使用 @Transactional 注解以声明方式控制回滚语义的详细信息,请参阅 @Transactional 设置spring-doc.cadn.net.cn

向 Spring 框架的事务基础设施表明事务工作需要回滚的推荐方式,是从当前在事务上下文中执行的代码中抛出一个 Exception。Spring 框架的事务基础设施代码会在异常沿调用栈向上冒泡时捕获任何未处理的 Exception,并据此判断是否将该事务标记为回滚。spring-doc.cadn.net.cn

在其默认配置下,Spring 框架的事务基础设施代码仅在发生运行时(unchecked)异常时将事务标记为回滚。 也就是说,当抛出的异常是 RuntimeException 的实例或其子类时。 (默认情况下,Error 实例也会导致回滚。)spring-doc.cadn.net.cn

从 Spring Framework 5.2 起,默认配置还提供了对 Vavr 的 Try 方法的支持,当该方法返回 'Failure' 时会触发事务回滚。 这使您可以使用 Try 以函数式风格处理错误,并在发生失败时自动回滚事务。有关 Vavr 的 Try 的更多信息,请参阅官方 Vavr 文档spring-doc.cadn.net.cn

以下是如何在事务方法中使用 Vavr 的 Try 的示例:spring-doc.cadn.net.cn

@Transactional
public Try<String> myTransactionalMethod() {
	// If myDataAccessOperation throws an exception, it will be caught by the
	// Try instance created with Try.of() and wrapped inside the Failure class
	// which can be checked using the isFailure() method on the Try instance.
	return Try.of(delegate::myDataAccessOperation);
}

在默认配置下,从事务方法中抛出的受检异常(checked exceptions)不会导致事务回滚。 你可以通过指定回滚规则(rollback rules),精确配置哪些Exception类型会将事务标记为回滚,包括受检异常。spring-doc.cadn.net.cn

回滚规则

回滚规则用于确定在抛出特定异常时是否应回滚事务,这些规则基于异常类型或异常模式。spring-doc.cadn.net.cn

回滚规则可以通过 rollback-forno-rollback-for 属性在 XML 中进行配置,这些属性允许将规则定义为模式。当使用 @Transactional 时,回滚规则可以通过 rollbackFor/noRollbackForrollbackForClassName/noRollbackForClassName 属性进行配置,这些属性分别允许基于异常类型或模式来定义规则。spring-doc.cadn.net.cn

当使用异常类型定义回滚规则时,该类型将用于与抛出的异常类型及其父类型进行匹配,从而提供类型安全性,并避免在使用模式匹配时可能出现的意外匹配。例如,值 jakarta.servlet.ServletException.class 仅会匹配类型为 jakarta.servlet.ServletException 及其子类的抛出异常。spring-doc.cadn.net.cn

当使用异常模式定义回滚规则时,该模式可以是异常类型的完全限定类名,也可以是其完全限定类名的子字符串(该异常类型必须是 Throwable 的子类),目前不支持通配符。例如,值为 "jakarta.servlet.ServletException""ServletException" 将匹配 jakarta.servlet.ServletException 及其子类。spring-doc.cadn.net.cn

您必须仔细考虑模式的具体程度,以及是否包含包信息(这并非强制要求)。例如,"Exception" 几乎会匹配所有内容,很可能会掩盖其他规则。"java.lang.Exception" 才是正确的写法,如果本意是使用 "Exception" 为所有受检异常(checked exceptions)定义一条规则的话。而对于像 "BaseBusinessException" 这样更具唯一性的异常名称,通常无需在异常模式中使用完整的限定类名。spring-doc.cadn.net.cn

此外,基于模式的回滚规则可能会意外匹配名称相似的异常和嵌套类。这是因为,只要抛出的异常的名称中包含为回滚规则配置的异常模式,该异常就会被视为与该基于模式的回滚规则匹配。例如,如果配置了一条规则以匹配 "com.example.CustomException",那么该规则将匹配名为 com.example.CustomExceptionV2 的异常(与 CustomException 位于同一包中,但带有额外后缀)或名为 com.example.CustomException$AnotherException 的异常(在 CustomException 中声明为嵌套类的异常)。spring-doc.cadn.net.cn

以下 XML 片段演示了如何通过在 Exception 属性中提供 异常模式 来为一个检查过的、应用程序特定的 rollback-for 类型配置回滚:spring-doc.cadn.net.cn

<tx:advice id="txAdvice" transaction-manager="txManager">
	<tx:attributes>
		<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
		<tx:method name="*"/>
	</tx:attributes>
</tx:advice>

如果你不希望在抛出异常时回滚事务,也可以指定“不回滚”规则。以下示例告诉 Spring 框架的事务基础设施,即使遇到未处理的 InstrumentNotFoundException,也要提交相应的事务:spring-doc.cadn.net.cn

<tx:advice id="txAdvice">
	<tx:attributes>
		<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
		<tx:method name="*"/>
	</tx:attributes>
</tx:advice>

当 Spring Framework 的事务基础设施捕获到一个异常,并根据配置的回滚规则来判断是否将事务标记为回滚时,匹配度最高的规则将生效。因此,在以下配置的情况下,除了 InstrumentNotFoundException 以外的任何异常都会导致相关事务回滚:spring-doc.cadn.net.cn

<tx:advice id="txAdvice">
	<tx:attributes>
		<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
	</tx:attributes>
</tx:advice>

你也可以通过编程方式显式地指示需要回滚。尽管这种方式很简单,但它具有很强的侵入性,并且会使你的代码与 Spring 框架的事务基础设施紧密耦合。以下示例展示了如何以编程方式指示需要回滚:spring-doc.cadn.net.cn

public void resolvePosition() {
	try {
		// some business logic...
	} catch (NoProductInStockException ex) {
		// trigger rollback programmatically
		TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
	}
}
fun resolvePosition() {
	try {
		// some business logic...
	} catch (ex: NoProductInStockException) {
		// trigger rollback programmatically
		TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
	}
}

如果可能的话,强烈建议您使用声明式方式来实现回滚。只有在绝对必要时才应使用编程式回滚,因为它的使用违背了实现干净的基于 POJO 架构的目标。spring-doc.cadn.net.cn