インタビュアーは私に2つの@Transactionalアノテーション付きの無効なシナリオに名前を付けるように頼みました、そして私は彼に一息で6つを言いました

前書き

@Transactionalアノテーションは、誰にとってもなじみのないものではありません。通常の開発で一般的に使用されるアノテーションであり、メソッド内の複数のデータベース操作が同時に成功または失敗することを保証できます。** @ Transactionalアノテーションを使用するときは、多くの詳細に注意を払う必要があります。そうしないと、 @ Transactional **が常に不可解に失敗することに気付くでしょう

以下では、インタビュアーの質問に何を、どこで、いつ、という4つの側面から答える方法完全に理解しています。

1.トランザクションとは(何)

トランザクションとは、一般的に、実行または実行されることを指します。コンピュータ用語では、データベース内のさまざまなデータ項目にアクセスして更新する可能性のあるプログラム実行ユニット(ユニット)を指します。

ここでは、引き出しの例を挙げて説明します。たとえば、ATMで1,000元を引き出す場合、大まかに2つの手順があります。最初の手順はパスワードの金額を入力することで、2番目の手順は1,000元を差し引くことです。ステップはATMから1,000元を引き出すことです。これらのステップの両方を実行するか、どちらも実行しないでください。銀行カードが1,000元を差し引いたが、ATMが支払わなかった場合、1,000元を失います。銀行カードが差し引かれたが、ATMが1,000元を支払った場合、銀行は1,000元を失います。

これらの2つのステップの一方が異常にならず、もう一方が正常に実行されるようにするにはどうすればよいですか?トランザクションは、そのような問題を解決するために使用されます。トランザクションは一連のアクションです。これらは統合されて完全な作業単位を形成します。これらのアクションはすべて完了する必要があります。1つが失敗すると、トランザクションは何も起こらなかったかのように初期状態にロールバックします。エンタープライズアプリケーションの開発では、トランザクション管理はデータの整合性と一貫性を確保するために不可欠なテクノロジーです。

私たちの日々の発展において、事柄は宣言的な事柄とプログラム的な事柄に分けられます。

プログラマティックトランザクション

コード内のトランザクションのコミットおよびロールバック操作の手動管理を指し、コードは比較的煩わしいものです。

プログラマティックトランザクションとは、コーディングによるトランザクションの実装を指し、ユーザーがコード内のトランザクションの境界を正確に定義できるようにします。

これは、トランザクション管理を実現するためのJDBCプログラミングに似ています。TransactionTemplateの使用を管理するか、基盤となるPlatformTransactionManagerを直接使用します。

プログラムによるトランザクション管理の場合、SpringではTransactionTemplateの使用をお勧めします。

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

宣言型トランザクション

管理はAOPに基づいています。その本質は、メソッドの前後をインターセプトし、ターゲットメソッドが開始する前にトランザクションを作成または追加し、ターゲットメソッドの実行後の実行に応じてトランザクションを送信またはロールバックすることです。

宣言型トランザクションの最大の利点は、プログラミングによってトランザクションを管理する必要がないため、ビジネスロジックコードにトランザクション管理コードをドープする必要がないことです。構成ファイル(または構成ファイル)で関連するトランザクションルール宣言を行うだけで済みます。 @Transactional Annotationを介して)、トランザクションルールをビジネスロジックに適用できます。

簡単に言えば、プログラマティックトランザクションはビジネスコードに侵入しますが、より詳細なトランザクション管理を提供します。

宣言型トランザクションはAOPに基づいているため、トランザクション管理の役割を果たすだけでなく、ビジネスコードの特定の実装にも影響を与えません。

宣言型トランザクションを実装する方法も2つあります。1つはTXおよびAOPxml構成ファイルに基づいており、もう1つは@Transactionalアノテーションに基づいています。

@GetMapping("/user")
@Transactional
public String user() {
    
    
       int insert = userMapper.insert(userInfo);
}

2. @Transactionalはどこで使用できますか(WHERE)

1. @Transactionalアノテーションはどこで使用できますか?

@Transactionalは、インターフェース、クラス、およびクラスメソッドに作用できます

  • クラスでの動作:@Transactionalアノテーションがクラスでリリースされると、クラスのすべてのパブリックメソッドが同じトランザクション属性情報で構成れていることを意味します。
  • メソッドへの作用:クラスが@Transactionalで構成されており、メソッドも@Transactionalで構成されている場合、メソッドのトランザクションはクラスのトランザクション構成情報をオーバーライドします。
  • インターフェイスでの動作:この使用方法はお勧めしません。インターフェイスでマークされ、Spring AOPがCGLib動的プロキシを使用するように構成されると、@ Transactionalアノテーションが無効になるためです。
@Transactional
@RestController
@RequestMapping
public class MybatisPlusController {
    
    
   @Autowired
   private UserMapper userMapper;
   
   @Transactional(rollbackFor = Exception.class)
   @GetMapping("/user")
   public String test() throws Exception {
    
    
       User user = new User();
       user.setName("javaHuang");
       user.setAge("2");
       user.setSex("2");
       int insert = userMapper.insert(cityInfoDict);
       return insert + "";
  }
}

2. @ Transactional属性の詳細な説明

伝播属性

トランザクションに代わって伝播伝播動作、デフォルト値Propagation.REQUIRED、その他の属性情報は次のとおりです。

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

分離プロパティ

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

TransactionDefinition.ISOLATION_DEFAULT
これはデフォルト値であり、基礎となるデータベースのデフォルトの分離レベルが使用されることを意味します。ほとんどのデータベースでは、この値は通常

TransactionDefinition.ISOLATION_READ_UNCOMMITTED
分離レベルは、トランザクションが別のトランザクションによって変更されたデータを読み取ることができるが、まだコミットされていないことを示します。このレベルでは、ダーティリード、繰り返し不可能なリード、ファントムリードを防ぐことはできないため、この分離レベルが使用されることはめったにありません。たとえば、PostgreSQLには実際にはこのレベルがありません。

TransactionDefinition.ISOLATION_READ_COMMITTED
この分離レベルは、トランザクションが別のトランザクションによってコミットされたデータのみを読み取ることができることを意味します。このレベルはダーティリードを防ぐことができます。これはほとんどの場合推奨値でもあります。

TransactionDefinition.ISOLATION_REPEATABLE_READ
この分離レベルは、トランザクションがプロセス全体でクエリを複数回実行でき、返されるレコードが毎回同じであることを示します。このレベルでは、ダーティ読み取りと繰り返し不可能な読み取りを防ぐことができます。

TransactionDefinition.ISOLATION_SERIALIZABLE
すべてのトランザクションが1つずつ実行されるため、トランザクション間の干渉はありません。つまり、このレベルでは、ダーティリード、繰り返し不可能なリード、およびファントムリードを防ぐことができます。しかし、これはプログラムのパフォーマンスに深刻な影響を及ぼします。通常、このレベルも使用されません。

タイムアウト属性

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

readOnly属性

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

rollbackFor属性

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

noRollbackFor属性

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

3. @Transactionalが失敗するのはいつですか(WHEN)

インタビュアーから直接、@ Transactionalを使ったことがあるかと聞かれました。これまで使ったことがないとは言えませんが、よく使われていると思います。

インタビュアーはもう一度私に尋ねました、実際の開発プロセス中に@Transactionalの失敗に遭遇したことはありますか?私は絶対にノーとは言えません。私はそれを非常に自信を持って何度も言いました。

インタビュアーの顔には疑問符が付いていることがよくありますか?次に、@ Transactionalがいつ失敗するか教えてください。

以下の内容は、インタビューで述べた失敗シナリオを整理したものです。

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

パブリックに変更されていないメソッドにTransactionalアノテーションが適用されると、Transactionalは無効になります。

失敗の理由は、Spring AOPがプロキシしている場合、TransactionInterceptor(トランザクションインターセプター)がターゲットメソッドの実行の前後にインターセプトするためです。DynamicAdvisedInterceptor(CglibAopProxyの内部クラス)のインターセプトメソッドまたはJdkDynamicAopProxyのinvokeメソッドは間接的にcomputeTransactionAttribute`を呼び出します。 AbstractFallbackTransactionAttributeSourceのメソッド。トランザクションアノテーションのトランザクション構成情報を取得します。

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

Modifier.isPublicは、ターゲットメソッドの修飾子がパブリックであるかどうかを確認します。パブリックでない場合、@ Transactionalの属性構成情報を取得しません。

注:@Transactionalアノテーションは、保護されたメソッドとプライベートに変更されたメソッドで使用されます。トランザクションは無効ですが、エラーは発生しません。これは、ミスを許容できる点です。

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

データベースエンジンはトランザクションをサポートする必要があります。MySQLの場合、テーブルはinnodbなどのトランザクションをサポートするエンジンを使用する必要があることに注意してください。myisamの場合、トランザクションは機能しません。

3. @伝播の設定が正しくないため、アノテーションが無効です

上記の伝播属性を解釈すると、次のことがわかります。

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

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

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

伝播属性を上記の3つに設定すると、@ Transactionalアノテーションは効果がありません

4. rollbackFor設定が間違っており、@ Transactionalアノテーションが無効です

上記のrollbackFor属性を解釈すると、次のことがわかります。

rollbackForは、トランザクションのロールバックをトリガーできる例外のタイプを指定できます。

デフォルトでは、Springはチェックされていないチェックされていない例外(RuntimeExceptionから継承された例外)またはエラーをスローしてトランザクションをロールバックします。

その他の例外は、ロールバックトランザクションをトリガーしません。トランザクションで他のタイプの例外をスローしたが、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);
}

5.メソッド間の相互呼び出しも@Transactionalの失敗を引き起こします

次のシナリオを見てみましょう。

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

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

  //@Transactional
   @GetMapping("/user")
   private Integer A() throws Exception {
    
    
       User user = new User();
       user.setName("javaHuang");
       /**
        * B 插入字段为 topJavaer的数据
        */
       this.insertB();
       /**
        * A 插入字段为 2的数据
        */
       int insert = userMapper.insert(user);

       return insert;
  }

   @Transactional()
   public Integer insertB() throws Exception {
    
    
       User user = new User();
       user.setName("topJavaer");
       return userMapper.insert(user);
  }

6.例外はキャッチによって「食べられ」、@ Transactionalが失敗する原因になります

この状況は、最も一般的な@Transactionalアノテーション無効化シナリオです。

  @Transactional
  private Integer A() throws Exception {
    
    
      int insert = 0;
      try {
    
    
          User user = new User();
          user.setCityName("javaHuang");
          user.setUserId(1);
          /**
            * A 插入字段为 javaHuang的数据
            */
          insert = userMapper.insert(user);
          /**
            * B 插入字段为 topJavaer的数据
            */
          b.insertB();
      } catch (Exception e) {
    
    
          e.printStackTrace();
      }
  }

メソッドBが例外をスローし、この時点でメソッドAがメソッドBの例外をキャッチしようとすると、トランザクションは正常にロールバックできませんが、例外がスローされます。

org.springframework.transaction.UnexpectedRollbackException:
ロールバックのみとしてマークされているため、トランザクションがロールバックされました

解決:

最初のトランザクションが宣言されたときにrollback = 'exception'を追加
し、2番目のcathコードブロックで手動でロールバックします

総括する

@Transactionalアノテーションを使用することがよくありますが、それがトランザクションアノテーションであることがわかっていることがよくあります。トランザクションアノテーションが失敗すると、私たち全員が途方に暮れることがよくあります。なぜ時間がかかるのかわかりません。できません。それを解決します。

この記事を通じて、@ Transactionalアノテーションの無効化シナリオについて学びました。将来このような状況に遭遇した場合、基本的には一目でそれを確認でき、滑らかな額、曽我に簡単に触れることができます。

お母さんは、私が書いたバグが見つからないことを心配する必要がなくなりました。

転載元:Java Architect's Road to Godユーザーコラム
元のリンク:https//segmentfault.com/a/1190000022219486

おすすめ

転載: blog.csdn.net/qq_35448165/article/details/108762248