@Transcational トランザクション アノテーションの失敗シナリオ分析

[序文] このメモは主に、@Transcational アノテーションが使用されている場合にトランザクションが失敗する原因となるシナリオを記録し、ピットや鉱山をクリアするために使用されます。

シナリオ 1. トランザクション管理が有効になっていない

Spring アプリケーションで @Transactional アノテーションを使用してトランザクションを管理するには、スタートアップ クラスに @EnableTransactionManagement アノテーションを追加する必要があります。このアノテーションの機能は、Spring のトランザクション マネージャーを開き、@Transactional でアノテーションが付けられたクラスを管理することです。@EnableTransactionManagement アノテーションが追加されていない場合、@Transactional アノテーションは効果がなく、トランザクションを管理できません。

したがって、@Transactional アノテーションが無効であるという問題が発生した場合は、 @EnableTransactionManagement アノテーションがスタートアップ クラスに追加されているかどうかを確認できます。追加されていない場合は、スタートアップ クラスにこのアノテーションを追加して、トランザクション マネージャーを開始できます。

@SpringBootApplication
@EnableTransactionManagement // 开启事务管理器
public class MyApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(MyApplication.class, args);
    }
}

SpringBoot フレームワークを使用すると、自動的にトランザクション管理が有効になります。SpringBoot アプリケーションのスタートアップ クラスには @SpringBootApplication アノテーションが付けられます。@SpringBootApplication アノテーションは、実際には @EnableAutoConfiguration アノテーションと他のアノテーションの組み合わせです。

ここに画像の説明を挿入

@EnableAutoConfiguration アノテーションでは、AutoConfigurationImportSelector.class セレクターの導入を確認できます。
ここに画像の説明を挿入

AutoConfigurationImportSelector のソース コードを直接確認し、selectImports メソッドで getAutoConfigurationEntry メソッドを呼び出します。
ここに画像の説明を挿入

メソッドを入力すると、getCandidateConfigurations のメソッドが表示されます
ここに画像の説明を挿入

このメソッドに入ると、このメソッドが spring.factories ファイルを読み取ることがわかります。プロジェクトの外部ライブラリから org.springframework.boot:spring-boot-autoconfigure を見つけ、META-INF フォルダーを開き、 spring ディレクトリ 注釈付きファイル:

ここに画像の説明を挿入

ファイル内のトランザクションに関連する TransactionAutoConfiguration クラスを確認できます。
ここに画像の説明を挿入

関連する説明からわかるように、このクラスを見つけます。このクラスは自動アセンブリ トランザクションに関連しています。
ここに画像の説明を挿入

このクラスでは、@EnableTransactionManagement の有効化アノテーションを見ました。
ここに画像の説明を挿入

シナリオ 2. データベース エンジンがトランザクションをサポートしていない

この状況の確率は高くありません. トランザクションが有効になるかどうか、またはデータベース エンジンがトランザクションをサポートしているかどうかが重要です. 一般的に使用される MySQL データベースは、デフォルトでトランザクションをサポートする innodb エンジンを使用します。データベース エンジンがトランザクションをサポートしない myisam に切り替えられると、トランザクションは基本的に無効になります。

シナリオ 3. @Transactional がパブリックではない変更されたメソッドに適用される

@Transactional装飾されていないメソッドにアノテーションを適用してもpublic、効果はありません。これは、Spring AOP がプロキシされると、TransactionInterceptor(トランザクション インターセプター) がターゲット メソッドの実行の前後にインターセプトするためです。このプロセス中に、インターセプターのinterceptメソッドは間接的に のメソッドをAbstractFallbackTransactionAttributeSource呼び出して、アノテーションのトランザクション構成情報を取得します。computeTransactionAttributeTransactional

computeTransactionAttributeメソッドでは、対象メソッドの修飾子がチェックされますpublicそうでない場合public、 の属性構成情報@Transactionalは。

/**
	 * Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
	 * {@link #getTransactionAttribute} is effectively a caching decorator for this method.
	 * <p>As of 4.1.8, this method can be overridden.
	 * @since 4.1.8
	 * @see #getTransactionAttribute
	 */
	@Nullable
	protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    
    
		// Don't allow non-public methods, as configured.
		if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
    
    
			return null;
		}
		...
	}

注: この場合、トランザクションは無効ですが、エラーは報告されません. これは落とし穴を避ける必要がある場所です.

シナリオ 4: プログラムで try/catch が例外を処理する

@Transactional アノテーションを入力してその詳細を表示すると、rollback 属性で次の要件を確認できます。

デフォルトでは、ロールバックは Error または RuntimeException が発生した場合にのみトリガーされます。したがって、例外がキャッチされてもスローされない場合、トランザクションのロールバックは有効になりません。

ここに画像の説明を挿入

通常、トランザクションを必要とするビジネス メソッドは例外をキャッチする必要はありません. キャッチする必要がある場合は、throw new RuntimeException() をスローするか、アノテーションでスローする例外の種類 @Transactional(rollbackFor=Exception.class) を指定する必要があります。無効になります。

シナリオ 5. @Transactional アノテーション属性 rollbackFor が正しく設定されていない

rollbackFor は、トランザクションのロールバックをトリガーできる例外の種類を指定できます。デフォルトでは、Spring はチェックされていない未チェックの例外 (RuntimeException から継承) またはエラーをスローしてトランザクションをロールバックしますが、他の例外はロールバック トランザクションをトリガーしません。別のタイプの例外またはこの例外のサブクラスがトランザクションでスローされたときに Spring がトランザクションをロールバックすると予想される場合は、rollbackFor 属性を指定する必要があります。

例は次のとおりです。

@Transactional(rollbackFor = Exception.class)
シナリオ 6. 同じクラスのメソッド呼び出しにより @Transactional が失敗する (コミットしやすい)

開発中、同じクラス内でメソッドを呼び出すことは避けられません.たとえば、クラス Test があり、そのメソッド A があり、A はこのクラスのメソッド B を呼び出しますが、メソッド A はアノテーショントランザクションを宣言しませんが、メソッドBはそうします。メソッド A が外部から呼び出された後、メソッド B のトランザクションは機能しません。ここもミスが多いところです。
理由: 実際には、これは Spring AOP プロキシの使用が原因です。トランザクション メソッドが現在のクラス以外のコードによって呼び出された場合にのみ、Spring によって生成されたプロキシ オブジェクトによって管理されるためです。

Spring は、Bean のスキャン時にアノテーション付きメソッドのサブクラス (つまり、プロキシ クラス プロキシ) を動的に生成し、その後、アノテーション付きメソッドが呼び出されると、実際にはプロキシによって呼び出され、プロキシは呼び出しの前に開始されます。取引。ただし、このアノテーション付きメソッドが同じクラスの他のメソッドによって呼び出される場合、メソッドはプロキシ経由ではなく、元の Bean 経由で直接呼び出されるため、トランザクションは開始されません。つまり、 @Transactional アノテーションは無効です。

シナリオ 7. Spring 以外のコンテナー管理クラスで @Transactional アノテーションを使用する

Spring コンテナで管理されていないクラスで @Transactional アノテーションを使用すると、Spring AOP はクラス内のメソッドをインターセプトできないため、トランザクション管理が有効になりません。

シナリオ 8: DDL ステートメントをロールバックできない

MySQL では、DDL ステートメントは非トランザクションです。これは、DDL ステートメントをロールバックできないことを意味します。DDL ステートメントがトランザクションで実行されると、トランザクションが暗黙的にコミットされるため、トランザクションで以前に実行された DML 操作はロールバックできません。

たとえば、テーブルのクリア操作の場合、truncate を使用してテーブルをクリアすると、トランザクションはロールバックできませんが、delete コマンドとして書き換えると、データは正常にロールバックできます。

# DDL 无法回滚
truncate tabelA;
# DML 可以回滚
delete from tabeA;

MySQL 8.0 では、トランザクション内の DDL ステートメントのロールバックがサポートされています。MySQL 8.0 より前のバージョンでは、DDL ステートメントがトランザクション内で実行されると、トランザクションが暗黙的にコミットされます。トランザクションでロールバックを実行すると、トランザクション内の DML ステートメントのみがロールバックされますが、DDL ステートメントはロールバックできません。

したがって、DDL ステートメントによってトランザクションが中断されるのを回避するには、トランザクション内で DDL ステートメントを使用しないようにする必要があります。DDL ステートメントを使用する必要がある場合は、DDL ステートメントと DML ステートメントを別々に実行するか、MySQL 8.0 以降を使用する必要があります。

おすすめ

転載: blog.csdn.net/zhzh980/article/details/129887764