宣言的トランザクションのロールバック
前のセクションでは、アプリケーションで宣言的にクラス(通常はサービスレイヤークラス)のトランザクション設定を指定する方法の基本について概説しました。このセクションでは、XML 構成で単純な宣言型の方法でトランザクションのロールバックを制御する方法について説明します。@Transactional
アノテーションを使用して宣言的にロールバックセマンティクスを制御する方法の詳細については、@Transactional
設定を参照してください。
Spring Framework のトランザクションインフラストラクチャに、トランザクションの作業をロールバックすることを示す推奨方法は、トランザクションのコンテキストで現在実行されているコードから Exception
をスローすることです。Spring Framework のトランザクションインフラストラクチャコードは、未処理の Exception
をキャッチし、コールスタックをバブルアップさせ、トランザクションをロールバック用にマークするかどうかを決定します。
デフォルトの構成では、Spring Framework のトランザクションインフラストラクチャコードは、実行時の非チェック例外の場合にのみ、ロールバックのトランザクションをマークします。つまり、スローされた例外が 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
) の戻り値にも特別な処理があり、元のメソッドから返された時点で例外的に完了していた場合は、そのようなハンドルのロールバックがトリガーされます。これは、実際のメソッド実装が CompletableFuture
シグネチャー (実行時に @Async
処理によってプロキシへの呼び出しの実際の非同期ハンドルに自動的に適応される) に準拠する必要がある可能性がある @Async
メソッドを対象としており、例外を再スローするのではなく、返されたハンドルで公開することを優先します。
Java
@Transactional @Async
public CompletableFuture<String> myTransactionalMethod() {
try {
return CompletableFuture.completedFuture(delegate.myDataAccessOperation());
}
catch (DataAccessException ex) {
return CompletableFuture.failedFuture(ex);
}
}
トランザクションメソッドからスローされたチェック済み例外は、既定の構成ではロールバックされません。ロールバックルールを指定することで、チェック済み例外を含め、どの Exception
型がトランザクションをロールバックするようにマークするかを正確に構成できます。
ロールバックルール ロールバックルールは、特定の例外がスローされたときにトランザクションをロールバックする必要があるかどうかを決定し、ルールは例外型または例外パターンに基づいています。 ロールバックルールは、 ロールバックルールが例外型で定義されている場合、その型はスローされた例外の型とそのスーパー型との照合に使用され、型の安全性を提供し、パターンの使用時に発生する可能性のある意図しない一致を回避します。例: ロールバックルールが例外パターンで定義されている場合、パターンは完全修飾クラス名または例外型の完全修飾クラス名のサブストリング(
|
次の 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 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 ベースのクリーンなアーキテクチャの実現に直面しています。