回滾宣告式事務

前面一節概述瞭如何在應用中宣告式地為類(通常是服務層類)指定事務設定的基礎知識。本節描述瞭如何在 XML 配置中以簡單、宣告式的方式控制事務的回滾。有關使用 @Transactional 註解宣告式控制回滾語義的詳細資訊,請參閱 @Transactional 設定。

向 Spring Framework 的事務基礎設施指示事務工作需要回滾的推薦方式是在當前在事務上下文中執行的程式碼中丟擲一個 Exception。Spring Framework 的事務基礎設施程式碼會捕獲呼叫棧中冒泡上來的任何未處理的 Exception,並決定是否將事務標記為回滾。

在其預設配置中,Spring Framework 的事務基礎設施程式碼僅在發生執行時 unchecked 異常時才將事務標記為回滾。也就是說,當丟擲的異常是 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);
	}
}

從事務方法中丟擲的 checked 異常在預設配置下不會導致回滾。您可以透過指定回滾規則來精確配置哪些 Exception 型別會標記事務回滾,包括 checked 異常。

回滾規則

回滾規則確定當丟擲給定異常時是否應回滾事務,這些規則基於異常型別或異常模式。

回滾規則可以在 XML 中透過 rollback-forno-rollback-for 屬性配置,這些屬性允許將規則定義為模式。使用 @Transactional 時,回滾規則可以透過 rollbackFor / noRollbackForrollbackForClassName / noRollbackForClassName 屬性配置,這些屬性允許分別基於異常型別或模式定義規則。

當回滾規則使用異常型別定義時,該型別將用於匹配丟擲異常的型別及其超型別,從而提供型別安全性並避免在使用模式時可能發生的任何意外匹配。例如,值 jakarta.servlet.ServletException.class 將僅匹配型別為 jakarta.servlet.ServletException 及其子類的丟擲異常。

當回滾規則使用異常模式定義時,模式可以是異常型別(必須是 Throwable 的子類)的完全限定類名或其子字串,目前不支援萬用字元。例如,值 "jakarta.servlet.ServletException""ServletException" 將匹配 jakarta.servlet.ServletException 及其子類。

您必須仔細考慮模式的特異性以及是否包含包資訊(這不是強制性的)。例如,"Exception" 將匹配幾乎任何東西,並且可能會隱藏其他規則。如果 "Exception" 旨在為所有 checked 異常定義規則,那麼 "java.lang.Exception" 才是正確的。對於更獨特的異常名稱,例如 "BaseBusinessException",可能不需要使用完全限定類名作為異常模式。

此外,基於模式的回滾規則可能會意外匹配名稱相似的異常和巢狀類。這是因為如果丟擲異常的名稱包含為回滾規則配置的異常模式,則認為丟擲的異常與給定的基於模式的回滾規則匹配。例如,對於配置為匹配 "com.example.CustomException" 的規則,該規則將匹配名為 com.example.CustomExceptionV2 的異常(與 CustomException 在同一包中但帶有附加字尾的異常)或名為 com.example.CustomException$AnotherException 的異常(在 CustomException 中宣告為巢狀類的異常)。

以下 XML 片段演示瞭如何透過 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>

如果您不希望在丟擲異常時回滾事務,也可以指定“不回滾”規則。以下示例指示 Spring Framework 的事務基礎設施,即使遇到未處理的 InstrumentNotFoundException,也要提交伴隨的事務。

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

當 Spring Framework 的事務基礎設施捕獲到異常並查閱配置的回滾規則以確定是否將事務標記為回滾時,最強的匹配規則生效。因此,在以下配置的情況下,除了 InstrumentNotFoundException 之外的任何異常都會導致伴隨事務的回滾。

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

您也可以透過程式設計式方式指示需要回滾。儘管簡單,但這個過程相當侵入性,並且將您的程式碼與 Spring Framework 的事務基礎設施緊密耦合。以下示例展示瞭如何程式設計式指示需要回滾。

  • 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 的架構相悖。