跳到主要内容
版本:7.0.3

回滚声明式事务

Hunyuan 7b 中英对照 Rolling Back a Declarative Transaction

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

向 Spring 框架的事务基础设施表明某项事务的工作需要回滚的推荐方法是,从当前在事务上下文中执行的代码中抛出一个 Exception。Spring 框架的事务基础设施代码会捕获任何未经处理的 Exception,随着调用栈的向上传播,它将决定是否标记该事务进行回滚。

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

默认配置还支持Vavr的Try方法,当该方法返回Failure时,会触发事务回滚。这使您能够使用Try来处理函数式风格的错误,并在发生失败时自动回滚事务。有关Vavr的Try的更多信息,请参考官方Vavr文档。以下是一个如何将Vavr的Try与事务性方法一起使用的示例:

@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)返回值也有特殊的处理方式:如果在从原始方法返回时该CompletableFuture异常地完成了(即提前结束了),则会触发对该CompletableFuture的回滚操作。这种处理机制主要是针对@Async方法设计的,因为在这些方法中,实际的方法实现可能需要遵循CompletableFuture的接口规范(在运行时,通过@Async的处理机制会自动将其适配为实际的异步处理结果)。这种方式更倾向于在返回的结果中体现这种异步特性,而不是重新抛出异常:

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

在默认配置下,从事务方法中抛出的受检异常(checked exceptions)不会导致事务回滚。你可以通过指定回滚规则(rollback rules)来精确配置哪些类型的Exception会触发事务回滚,包括受检异常。

备注

回滚规则
回滚规则决定了在抛出特定异常时是否应回滚事务,这些规则基于异常类型或异常模式。

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

当使用异常类型定义回滚规则时(例如,通过rollbackFor),该类型将用于与抛出的异常类型进行匹配。具体来说,给定一个配置的异常类型C,如果抛出的异常类型T等于C或是C的子类,则认为该异常与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片段展示了如何通过提供“exception pattern”(异常模式)并通过“rollback-for”属性来配置针对特定于应用程序的、被标记为“checked”的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>

如果你不希望在执行过程中抛出异常时回滚事务,也可以指定“no rollback”规则。以下示例告诉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框架的事务基础设施紧密耦合。以下示例展示了如何通过编程方式指示需要进行回滚:

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

如果可能的话,我们强烈建议使用声明式的回滚方法。在确实有必要的情况下,也可以使用编程式的回滚方式,但这种使用方式与实现基于POJO的简洁架构是相违背的。