回滚声明式事务

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

建议向 Spring 框架的事务基础设施表明事务工作需要回滚的方式是,在当前在事务上下文中执行的代码中抛出 Exception。Spring 框架的事务基础设施代码会捕获任何未处理的 Exception,因为它会冒泡到调用堆栈中,并确定是否将事务标记为回滚。

在其默认配置中,Spring 框架的事务基础设施代码仅在运行时未检查异常的情况下将事务标记为回滚。也就是说,当抛出的异常是 RuntimeException 的实例或子类时。(Error 实例也默认导致回滚)。

默认配置还支持 Vavr 的 Try 方法,当它返回“Failure”时触发事务回滚。这允许您使用 Try 处理函数式错误,并在失败时自动回滚事务。有关 Vavr 的 Try 的更多信息,请参阅官方 Vavr 文档。以下是如何将 Vavr 的 Try 与事务方法一起使用的示例

  • Java

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

从 Spring Framework 6.1 开始,还对 CompletableFuture(以及一般的 Future)返回值进行了特殊处理,如果此类句柄在从原始方法返回时异常完成,则会触发回滚。这适用于 @Async 方法,其中实际方法实现可能需要符合 CompletableFuture 签名(在运行时通过 @Async 处理自动适应代理调用的实际异步句柄),优先在返回的句柄中公开而不是重新抛出异常。

  • Java

@Transactional @Async
public CompletableFuture<String> myTransactionalMethod() {
	try {
		return CompletableFuture.completedFuture(delegate.myDataAccessOperation());
	}
	catch (DataAccessException ex) {
		return CompletableFuture.failedFuture(ex);
	}
}

在默认配置中,从事务方法抛出的检查异常不会导致回滚。您可以通过指定回滚规则来精确配置哪些 Exception 类型标记事务进行回滚,包括检查异常。

回滚规则

回滚规则确定在抛出给定异常时是否应回滚事务,并且这些规则基于异常类型或异常模式。

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

当使用异常类型定义回滚规则时——例如,通过 rollbackFor——该类型将用于与抛出异常的类型进行匹配。具体来说,给定一个配置的异常类型 C,如果 T 等于 CC 的子类,则类型为 T 的抛出异常将被视为与 C 匹配。这提供了类型安全性,并避免了在使用模式时可能发生的任何无意匹配。例如,值 jakarta.servlet.ServletException.class 将仅匹配类型为 jakarta.servlet.ServletException 及其子类的抛出异常。

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

您必须仔细考虑模式的特异性以及是否包含包信息(这不是强制性的)。例如,"Exception" 将匹配几乎所有内容,并且可能会隐藏其他规则。如果 "Exception" 旨在定义所有检查异常的规则,那么 "java.lang.Exception" 将是正确的。对于像 "BaseBusinessException" 这样更独特的异常名称,可能不需要为异常模式使用完全限定类名。

此外,基于模式的回滚规则可能会导致对名称相似的异常和嵌套类的无意匹配。这是因为如果抛出异常的名称包含为回滚规则配置的异常模式,则认为抛出的异常与给定基于模式的回滚规则匹配。例如,给定一个配置为匹配 "com.example.CustomException" 的规则,该规则将匹配名为 com.example.CustomExceptionV2(与 CustomException 在同一包中但带有附加后缀的异常)或名为 com.example.CustomException$AnotherException(在 CustomException 中声明为嵌套类的异常)的异常。

以下 XML 片段演示了如何通过 rollback-for 属性提供异常模式来配置检查的、特定于应用程序的 Exception 类型回滚

<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 时也要提交附带的事务

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

当 Spring 框架的事务基础设施捕获到异常并查阅配置的回滚规则以确定是否将事务标记为回滚时,最强的匹配规则获胜。因此,在以下配置中,除了 InstrumentNotFoundException 之外的任何异常都会导致附带事务的回滚

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

您还可以以编程方式指示所需的滚动回。虽然简单,但此过程具有侵入性,并使您的代码与 Spring 框架的事务基础设施紧密耦合。以下示例显示了如何以编程方式指示所需的滚动回

  • Java

  • Kotlin

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 的纯净架构相悖。

© . This site is unofficial and not affiliated with VMware.