6 @Transactionalアノテーションの失敗シナリオ

1.アフェアーズ

トランザクション管理システムの開発には、不可欠な部分であるSpring優れたトランザクション管理メカニズムを提供し、それが分割され编程式事务、および声明式事务2種類。

プログラムによるトランザクション:次の例のように、コードでのトランザクション送信、ロールバック、およびその他の操作の手動管理を指します。コードは比較的煩わしいものです。

try {
//TODO something
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
thrownew InvoiceApplyException("异常失败");
}

宣言型トランザクションAOPアスペクト指向に基づいて、特定のビジネスとトランザクション処理の部分を分離します。コードの侵入は非常に少ないため、実際の開発では宣言型トランザクションがより多く使用されます。また、二つの方法で実装宣言型トランザクションは、1をベースにTXし、AOPXML設定ファイルの方法、の2種類に基づいて@Transactional 、注釈付き。

@Transactional
@GetMapping("/test")
public String test() {

int insert = cityInfoDictMapper.insert(cityInfoDict);
}

2. @Transactionalの概要

1. @Transactionalアノテーションはどこで機能しますか?

@Transactional それは行動することができ接口类方法

  • クラスに対するアクション@Transactional 注解放在类上时,表示所有该类的 public メソッドが同じトランザクション属性情報で構成されている場合。
  • メソッドへの影響:クラスが構成される@Transactionalと、メソッドも構成@Transactionalされます。メソッド   のトランザクションは、クラスのトランザクション構成情報をオーバーライドします
  • インターフェースでの動作:この使用方法はお勧めできません。インターフェースでマークを付け、Spring AOPでCGLib動的プロキシを使用するように構成すると、@Transactionalアノテーション無効になるためです。
@Transactional
@RestController
@RequestMapping
publicclass MybatisPlusController {
@Autowired
private CityInfoDictMapper cityInfoDictMapper;

@Transactional(rollbackFor = Exception.class)
@GetMapping("/test")
public String test() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setParentCityId(2);
cityInfoDict.setCityName("2");
cityInfoDict.setCityLevel("2");
cityInfoDict.setCityCode("2");
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert + "";
}
}

2. @Transactionalアノテーションの属性は何ですか?

伝播属性

propagation トランザクションの伝播動作を表します。デフォルト値は  Propagation.REQUIRED次のとおりです。その他の属性情報は次のとおりです。

  • Propagation.REQUIRED:現在トランザクションがある場合はトランザクションに参加し、現在のトランザクションがない場合は新しいトランザクションを作成します。  つまり、メソッドAとメソッドBの両方に注釈が付けられている場合、デフォルトの伝播モードでは、メソッドAがメソッドBを内部的に呼び出し、2つのメソッドのトランザクションが1つのトランザクションにマージされます 
  • Propagation.SUPPORTS:現在トランザクションがある場合はトランザクションに参加し、トランザクションがない場合は非トランザクション的な方法で実行を続けます。
  • Propagation.MANDATORY:現在トランザクションがある場合はトランザクションが追加され、現在のトランザクションがない場合は例外がスローされます。
  • Propagation.REQUIRES_NEW:新しいトランザクションを再作成します。現在トランザクションがある場合は、現在のトランザクションを一時停止します。  クラスA Propagation.REQUIREDのメソッドaがデフォルトモード、クラスBのメソッドbにPropagation.REQUIRES_NEWモードを加えた  後、メソッドaでメソッドbを呼び出してデータベースを操作しますが、メソッドaが例外をスローした後、メソッドbはロールバックしません。Propagation.REQUIRES_NEWメソッドa )のトランザクションを一時停止する  ため
  • Propagation.NOT_SUPPORTED:非トランザクションの方法で実行します。現在トランザクションがある場合は、現在のトランザクションを一時停止します。
  • Propagation.NEVER:非トランザクションの方法で実行します。トランザクションが現在存在する場合は、例外がスローされます。
  • Propagation.NESTED :Propagation.REQUIREDと同じ効果。
分離特性

isolation :トランザクションの分離レベルIsolation.DEFAULTデフォルト値は  です。

  • TransactionDefinition.ISOLATION_DEFAULT:  バックエンドデータベースのデフォルトの分離レベル、MysqlのデフォルトのREPEATABLE_READ分離レベル、OracleのデフォルトのREAD_COMMITTED分離レベルを使用します。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:  最も低い分離レベル。コミットされていないデータ変更の読み取りを許可します。これにより、ダーティリード、ファントムリード、または繰り返し不可の読み取りが発生する可能性があります
  • TransactionDefinition.ISOLATION_READ_COMMITTED:  並行トランザクションによって送信されたデータの読み取りを許可し、ダーティリードを防ぐことができますが、マジックリードまたは繰り返し不可の読み取りが発生する可能性があります
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:  独自のトランザクションによってデータが変更されない限り、同じフィールドでの複数の読み取り結果は一貫しており、ダーティリードや繰り返し不可の読み取りを防ぐことができますが、マジックリードは引き続き発生します。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:  ACID分離レベルに完全に準拠した最高の分離レベル。すべてのトランザクションは1つずつ順番に実行されるため、トランザクション間の干渉の可能性はありません。つまり、このレベルでは、ダーティリード、繰り返し不可の読み取り、マジックリードを防ぐことができますしかし、これはプログラムのパフォーマンスに深刻な影響を及ぼします。通常、このレベルは使用されません。
タイムアウト属性

timeout :トランザクションのタイムアウト期間。デフォルト値は-1です。時間制限を超えてもトランザクションが完了していない場合、トランザクションは自動的にロールバックされます。

readOnly属性

readOnly :トランザクションが読み取り専用トランザクションかどうかを指定します。デフォルト値はfalseです。データの読み取りなど、トランザクションを必要としないメソッドを無視するには、読み取り専用をtrueに設定できます。

rollbackFor属性

rollbackFor :トランザクションのロールバックをトリガーできる例外のタイプを指定するために使用されます。複数の例外タイプを指定できます。

noRollbackFor属性**

noRollbackFor:指定した例外タイプをスローします。トランザクションをロールバックせずに複数の例外タイプを指定することもできます。

3つの@Transactional障害シナリオ

次に、特定のコードと組み合わせた特定のコードを分析してみましょう。@ Transactionalアノテーションは無効になります。

1. @Transactionalは非公開の装飾メソッドに適用されます

Transactional注釈がpublic 装飾されていないメソッドに適用された場合、トランザクションは無効になります。

無効な理由は、Spring AOPプロキシでは、上図TransactionInterceptor (トランザクションインターセプター)に示すように  、ターゲットメソッドの実行前後にDynamicAdvisedInterceptor、インターセプトメソッド(CglibAopProxyの内部クラス)または  JdkDynamicAopProxy invokeメソッドが間接的AbstractFallbackTransactionAttributeSourcecomputeTransactionAttribute メソッドを呼び出し  て  トランザクションを取得するためです。注釈付きトランザクション構成情報。

protected TransactionAttribute computeTransactionAttribute(Method method,
Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
returnnull;
}

このメソッドは、ターゲットメソッドの修飾子がpublicであるかどうか、@Transactional およびpublicでない場合は取得されないプロパティ構成情報をチェックします。

注:protectedprivate 修正された方法の使用して  @Transactional 、トランザクションが有効ではありませんが、コメントを、しかし、エラーは存在しません、これは間違いを犯すために私たちの非常に少ない容量です。

2. @Transactionalアノテーションプロパティの伝播が正しく設定されていない

この失敗は構成エラーが原因であり、次の3つの構成が正しく構成されていない場合、トランザクションはロールバックされません。

TransactionDefinition.PROPAGATION_SUPPORTS:現在トランザクションがある場合はトランザクションに参加し、現在トランザクションがない場合は非トランザクションの方法で実行を続けます。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED:非トランザクションモードで実行し、現在トランザクションがある場合は、現在のトランザクションを一時停止します。

TransactionDefinition.PROPAGATION_NEVER:非トランザクションモードで実行します。トランザクションが現在存在する場合は、例外がスローされます。

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

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

// 希望自定义的异常可以进行回滚
@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class

ターゲットメソッドでスローrollbackFor された例外指定された例外のサブクラスである  場合、トランザクションもロールバックされます。Springのソースコードは次のとおりです。

private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}

4.同じクラスのメソッド呼び出しにより、@ Transactionalが失敗する

開発中、同じクラスのメソッドを呼び出すことは避けられません。たとえば、メソッドTestがあり、そのメソッドA、Aの1つは、このクラスのメソッドBを呼び出します(メソッドBがパブリックまたはプライベートのどちらで変更されたかに関係なく)、メソッドAステートメントはトランザクションに注釈を付け、Bメソッドはそうします。メソッドAを外部から呼び出した後、メソッドBのトランザクションは機能しません。ここも間違いが多い場所です。

なぜこれが起こるのですか?実際、これはSpring AOPプロキシの使用が原因です。トランザクションメソッドが現在のクラス以外のコードによって呼び出された場合のみ、Spring生成されたプロキシオブジェクトによって管理されるためです。

//@Transactional
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/**
* B 插入字段为 3的数据
*/
this.insertB();
/**
* A 插入字段为 2的数据
*/
int insert = cityInfoDictMapper.insert(cityInfoDict);

return insert;
}

@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);

return cityInfoDictMapper.insert(cityInfoDict);
}

5.例外がキャッチによって「食べられ」、@ Transactionalが失敗する

この状況は、最も一般的なタイプの  @Transactional 注釈失敗シナリオです。

    @Transactional
private Integer A() throws Exception {
int insert = 0;
try {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
cityInfoDict.setParentCityId(2);
/**
* A 插入字段为 2的数据
*/
insert = cityInfoDictMapper.insert(cityInfoDict);
/**
* B 插入字段为 3的数据
*/
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}

メソッドBが内部で例外をスローし、メソッドAがこの時点でメソッドBの例外をキャッチしようとした場合、このトランザクションは正常にロールバックできますか?

回答:いいえ!

例外をスローします:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

ときので、ServiceB後に例外がスローされServiceB、現在のトランザクションのニーズを特定しますrollbackただしServiceA、この例外を手動でキャッチして処理するため、ServiceA現在のトランザクションは正常であるはずcommitです。このとき、矛盾がありました。つまり、このため、前のUnexpectedRollbackException例外がスローれました

springトランザクションは、ビジネスメソッドが呼び出される前、commit または  ビジネスメソッドが実行された後に開始rollbackされます。トランザクションが実行されるかどうかは、それがスローされるかどうかによって異なりruntime异常ます。スローさruntime exception れ、ビジネスメソッドにキャッチがない場合、トランザクションはロールバックされます。

通常、ビジネスメソッドでは例外のキャッチは必要ありません。キャッチする必要があるthrow new RuntimeException()場合は、例外をスローするか、アノテーションの例外タイプを指定する必要があります@Transactional(rollbackFor=Exception.class)。それ以外の場合は、トランザクションが失敗し、データのコミットによりデータの不整合が発生するため、キャッチを試行するとさらに追加されます。

6.データベースエンジンはトランザクションをサポートしていません

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

まとめ

@Transactional 注釈はシンプルで使いやすいように見えますが、その使用法について少し知らなければ、まだ多くの穴を踏むことになります。

おすすめ

転載: www.cnblogs.com/frankyou/p/12691463.html