Spring Boot 2.x 基本チュートリアル: JTA を使用した分散トランザクションの実装

Spring Boot 2.x 基本チュートリアル: JTA を使用した分散トランザクションの実装

[[380215]]

Spring Boot プロジェクトでは、複数のデータ ソースに接続するのが非常に一般的です。

複数のデータ ソースを使用する場合、データ ソース A とデータ ソース B への更新をトランザクション的に行うという特別なシナリオも発生します。このような例は非常に一般的です。たとえば、注文データベースに注文レコードを作成し、製品データベースの製品在庫を減算するなどです。在庫減算が失敗した場合は、注文の作成もロールバックする必要があります。

これら 2 つのデータが同じデータベース内にある場合は、先に紹介したトランザクション管理を通じて簡単に解決できます。ただし、2 つの操作が異なるデータベースで行われる場合、これは不可能です。

この記事では、この問題の解決策である JTA トランザクションを紹介します。

JTAとは

JTA、正式名称: Java Transaction API。 JTA トランザクションは、JDBC トランザクションよりも強力です。 JTA トランザクションには複数の参加者が存在する可能性がありますが、JDBC トランザクションは単一のデータベース接続に限定されます。したがって、複数のデータベースを同時に操作する場合、JTA トランザクションを使用すると、JDBC トランザクションの欠点を補うことができます。

Spring Boot 2.x では、次の 2 つの JTA 実装が統合されています。

Atomikos: spring-boot-starter-jta-atomikos 依存関係を導入することで使用可能

Bitronix: spring-boot-starter-jta-bitronix依存関係を導入することで使用可能

Bitronix は Spring Boot 2.3.0 以降では非推奨となっているため、以下のハンズオンセッションでは Atomikos を例に JTA の使い方を紹介します。

自分で試してみましょう

Spring Boot で JTA を使用して、複数のデータ ソースでトランザクション管理を実装する方法を見てみましょう。

準備

  • ここでは、最も基本的な JdbcTemplate を使用してデータ アクセスを実装します。そのため、JdbcTemplate を使用して複数のデータ ソースを構成する方法がわからない場合は、まず JdbcTemplate の複数のデータ ソース構成を確認することをお勧めします。

シーン設定:

  • test1とtest2という2つのライブラリがあるとします。
  • 両方のデータベースにUserテーブルがあり、これら2つのテーブルのデータが一致していることを期待しています。
  • 両方のテーブルにすでにレコードが存在するものとします: name=aaa, age=30; 2 つのテーブルのデータは一致しているため、更新が必要な場合は、両方のデータベースの User テーブルを更新する必要があり、両方とも成功するか、両方とも失敗します。

操作の詳細

pom.xml に JTA 実装 Atomikos Starter を追加します。

  1. <依存関係>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-jta-atomikos</artifactId>
  4. </依存関係>

application.properties 構成ファイルで 2 つの test1 および test2 データ ソースを構成します。

  1. spring.jta.enabled = 
  2.  
  3. spring.jta.atomikos.datasource.primary .xa -properties.url=jdbc:mysql://localhost:3306/test1
  4. spring.jta.atomikos.datasource.primary.xa -プロパティ.user =ルート
  5. spring.jta.atomikos.datasource.primary.xa -プロパティ.パスワード= 12345678
  6. spring.jta.atomikos.datasource.primary .xa -データソースクラス=com.mysql.cj.jdbc.MysqlXADataSource
  7. spring.jta.atomikos.datasource.primary.unique -リソース-名前= test1
  8. spring.jta.atomikos.datasource.primary.max -プール-サイズ= 25
  9. spring.jta.atomikos.datasource.primary.min -プール-サイズ= 3
  10. spring.jta.atomikos.datasource.primary.max -有効期間=20000
  11. spring.jta.atomikos.datasource.primary.borrow-接続-タイムアウト=10000
  12.  
  13. spring.jta.atomikos.datasource.secondary.xa-properties.url=jdbc:mysql://localhost:3306/test2
  14. spring.jta.atomikos.datasource.secondary.xa -properties.user =root
  15. spring.jta.atomikos.datasource.secondary.xa- properties.password =12345678
  16. spring.jta.atomikos.datasource.secondary.xa-データソースクラス=com.mysql.cj.jdbc.MysqlXADataSource
  17. spring.jta.atomikos.datasource.secondary です。一意のリソース=test2
  18. spring.jta.atomikos.datasource.secondary です。最大プールサイズ= 25
  19. spring.jta.atomikos.datasource.secondary です。最小プールサイズ=3
  20. spring.jta.atomikos.datasource.secondary.max -有効期間 = 20000
  21. spring.jta.atomikos.datasource.secondary.borrow-接続-timeout=10000

マルチデータソース構成クラスを作成する

  1. @構成
  2. パブリッククラス DataSourceConfiguration {
  3.  
  4. @主要な 
  5. @ビーン
  6. @ConfigurationProperties(プレフィックス = "spring.jta.atomikos.datasource.primary" )
  7. パブリックデータソースプライマリデータソース() {
  8. 新しい AtomikosDataSourceBean()を返します
  9. }
  10.  
  11. @ビーン
  12. @ConfigurationProperties(プレフィックス = "spring.jta.atomikos.datasource.secondary" )
  13. パブリックデータソースセカンダリデータソース() {
  14. 新しい AtomikosDataSourceBean()を返します
  15. }
  16.  
  17. @ビーン
  18. パブリックJdbcTemplate primaryJdbcTemplate(@Qualifier( "primaryDataSource" ) データソース primaryDataSource) {
  19. 新しい JdbcTemplate(primaryDataSource)を返します
  20. }
  21.  
  22. @ビーン
  23. パブリックJdbcTemplate secondaryJdbcTemplate(@Qualifier( "secondaryDataSource" ) データソース secondaryDataSource) {
  24. 新しい JdbcTemplate(secondaryDataSource)を返します
  25. }
  26.  
  27. }

ここでの異なる構成に加えて、DataSource は AtomikosDataSourceBean も使用することに注意してください。複数のデータ ソースの以前の構成で使用された構成クラスと実装クラスの違いに注意してください。

2 つの異なる状況をシミュレートするサービス実装を作成します。

  1. @サービス
  2. パブリッククラスTestService{
  3.  
  4. プライベート JdbcTemplate primaryJdbcTemplate;
  5. プライベート JdbcTemplate セカンダリ JdbcTemplate;
  6.  
  7. パブリックTestService(JdbcTemplate プライマリ JdbcTemplate、JdbcTemplate セカンダリ JdbcTemplate) {
  8. プライマリJdbcテンプレートを作成します。
  9. this.secondaryJdbcTemplate = セカンダリJdbcTemplate;
  10. }
  11.  
  12. @トランザクション
  13. パブリックvoidtx() {
  14. // test1ライブラリのデータを変更する
  15. プライマリJdbcテンプレート。更新( "更新ユーザー set age = ? where name = ?" 、 30、 "aaa" );
  16. // test2ライブラリのデータを変更する
  17. セカンダリJdbcテンプレート。更新( "更新ユーザー set age = ? where name = ?" 、 30、 "aaa" );
  18. }
  19.  
  20. @トランザクション
  21. パブリックボイドtx2() {
  22. // test1ライブラリのデータを変更する
  23. プライマリJdbcテンプレート。更新( "更新ユーザー set age = ? where name = ?" 、 40、 "aaa" );
  24. // シミュレーション: test2 ライブラリを変更する前に例外をスローします
  25. 新しい RuntimeException() をスローします。
  26. }
  27.  
  28. }

ここで、tx 関数は 2 つのステートメントの更新操作であり、通常は成功します。 tx2 関数では、例外を人為的に作成します。この例外は、test1 データベースのデータが更新された後に生成されます。このようにして、test1 の更新が成功したかどうか、また JTA を使用してロールバックできるかどうかをテストできます。

テストクラスを作成し、テストケースを書く

  1. @SpringBootTest(クラス = Chapter312Application.class)
  2. パブリッククラス Chapter312ApplicationTests {
  3.  
  4. オートワイヤード
  5. 保護された Jdbc テンプレート primaryJdbcTemplate;
  6. オートワイヤード
  7. 保護された JdbcTemplate セカンダリ JdbcTemplate;
  8.  
  9. オートワイヤード
  10. プライベート TestService testService;
  11.  
  12. @テスト
  13. パブリックvoid test1()は例外をスローします{
  14. // 正しい更新状況
  15. テストサービス.tx();
  16. Assertions.assertEquals(30, primaryJdbcTemplate.queryForObject( "select age from user where name=?" , Integer .class, "aaa" ));
  17. Assertions.assertEquals(30, secondaryJdbcTemplate.queryForObject( "select age from user where name=?" , Integer .class, "aaa" ));
  18. }
  19.  
  20. @テスト
  21. パブリックvoid test2() は例外をスローします {
  22. // 更新に失敗しました
  23. 試す {
  24. テストサービス.tx2();
  25. } キャッチ (例外 e) {
  26. e.printStackTrace();
  27. ついに
  28. // 部分的な更新に失敗しました。test1 の更新をロールバックする必要があります
  29. Assertions.assertEquals(30, primaryJdbcTemplate.queryForObject( "select age from user where name=?" , Integer .class, "aaa" ));
  30. Assertions.assertEquals(30, secondaryJdbcTemplate.queryForObject( "select age from user where name=?" , Integer .class, "aaa" ));
  31. }
  32. }
  33.  
  34. }

ここに 2 つのテストケースがあります:

  • テスト 1: 意図的な例外がないため、2 つのデータベースの更新は期待どおりに成功します。したがって、2 つのデータは name=aaa に従ってチェックされ、年齢が 30 に更新されているかどうかが確認されます。
  • test2: tx2 関数は、test1 の name=aaa のユーザーの年齢を 40 に更新し、例外をスローします。 JTA トランザクションが有効になると、経過時間は 30 にロールバックされます。したがって、ここでのチェックは、2 つのデータベース内の aaa ユーザーの経過時間が 30 である必要があるということです。つまり、JTA トランザクションが有効になり、test1 および test2 データベース内のユーザー テーブル データがダーティ データを作成せずに一貫して更新されることが保証されます。

テスト検証

上記の単体テストを実行します。

起動フェーズ中のログを見ると、次のような Atomikos 初期化ログ出力が確認できます。

  1. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.default_max_wait_time_on_shutdown = 9223372036854775807  
  2. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.allow_subtransactions = true  
  3. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.recovery_delay = 10000  
  4. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.automatic_resource_registration = true  
  5. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.oltp_max_retries = 5  
  6. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.client_demarcation = false  
  7. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.threaded_2pc = false  
  8. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.serial_jta_transactions = true  
  9. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.log_base_dir = /Users/didi/Documents/GitHub/SpringBoot-Learning/2.x/chapter3-12/transaction-logs  
  10. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.rmi_export_class = none  
  11. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.max_actives = 50  
  12. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.checkpoint_interval = 500  
  13. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.enable_logging = true  
  14. 2021-02-02 19:00:36.145 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.log_base_name = tmlog  
  15. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.max_timeout = 300000  
  16. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.trust_client_tm = false  
  17. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: java.naming.factory.initial = com.sun.jndi.rmi.registry.RegistryContextFactory  
  18. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.tm_unique_name = 127.0.0.1.tm  
  19. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.forget_orphaned_log_entries_delay = 86400000  
  20. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.oltp_retry_interval = 10000  
  21. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: java.naming.provider.url = rmi://localhost:1099  
  22. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.force_shutdown_on_vm_exit = false  
  23. 2021-02-02 19:00:36.146 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: USING: com.atomikos.icatch.default_jta_timeout = 10000  
  24. 2021-02-02 19:00:36.147 INFO 8868 --- [main] caicatch.provider.imp.AssemblerImp: デフォルト (ローカル) のログ記録とリカバリを使用しています...  
  25. 2021-02-02 19:00:36.184 INFO 8868 --- [main] cadxa.XATransactionalResource: test1: XAResource を更新しました 
  26. 2021-02-02 19:00:36.203 INFO 8868 --- [メイン] cadxa.XATransactionalResource  

同時に、トランザクションに関するログ情報は、transaction-logs ディレクトリでも見つかります。

  1. { "id" : "127.0.0.1.tm161226409083100001" "wasCommitted" : true "participants" :[{ "uri" : "127.0.0.1.tm1" "state" : "COMMITTING" "expires" :1612264100801、 "resourceName" : "test1" },{ "uri" : "127.0.0.1.tm2" "state" : "COMMITTING" "expires" :1612264100801、 "resourceName" : "test2" }]}
  2. { "id" : "127.0.0.1.tm161226409083100001" "wasCommitted" : true "participants" :[{ "uri" : "127.0.0.1.tm1" "state" : "TERMINATED" "expires" :1612264100804、 "resourceName" : "test1" },{ "uri" : "127.0.0.1.tm2" "state" : "TERMINATED" "expires" :1612264100804、 "resourceName" : "test2" }]}
  3. { "id" : "127.0.0.1.tm161226409092800002" "wasCommitted" : false "participants" :[{ "uri" : "127.0.0.1.tm3" "state" : "TERMINATED" "expires" :1612264100832、 "resourceName" : "test1" }]}

コードサンプル

この記事に関連する例については、次のリポジトリの chapter3-12 ディレクトリを参照してください。

Github: https://github.com/dyc87112/SpringBoot-Learning/

gitee: https://gitee.com/didispace/SpringBoot-Learning/

<<:  クラウド コンピューティングはビジネスとソフトウェア アーキテクチャに革命をもたらすことができるでしょうか?

>>:  Canalysの最新レポートによると、2020年の世界のクラウドインフラサービス支出は合計1,420億ドルになる見通し

推薦する

ウェブサイトの降格のさまざまな形式と解決策の体系的な説明

ウェブサイトを構築していると、権限が減ってしまうさまざまな状況に遭遇することが多く、これをKステーシ...

ウェブサイトのランキング低下の主な理由について簡単に説明します(分析結果)

みなさんこんにちは。私はバーチャルリアリティウェブサイトデザインです。最近、私のウェブサイトのランキ...

ウェブサイトが閉鎖され、コンテンツが失われ、ランキングが下がってしまったらどうすればいいでしょうか?

数日前、当社はサーバーを提供していたパートナーと小さな衝突を起こしました。スペースプロバイダーは実際...

ウェブサイトのタイトルの最適化は行いましたか?

新しいウェブサイトでも古いウェブサイトでも、ウェブサイトのタイトルは最適化において非常に重要な詳細で...

「ダブルイレブン」を解釈し、eコマースプラットフォームが安定したサーバー運用を確保する方法について議論する

毎年恒例のダブル11ショッピングカーニバルを解釈するとき、ネットユーザーの熱狂的な購買シーンと国内主...

企業ウェブサイトランキングの不安定さはウェブサイトのキーワードに関連している

ショートビデオ、セルフメディア、インフルエンサーのためのワンストップサービス今日の検索エンジンは、最...

サービス - $10/年/5G ハードドライブ/25g トラフィック/cPanel パネル/仮想ホスト

Srvis を知らない人や聞いたことがない人はまだまだ多いのではないでしょうか。実際、Console...

Googleの障害事件からインターネットの動作原理を理解する

本日、Google のサービスに約 27 分間の短時間の停止が発生し、一部の地域のインターネット ユ...

オンライン移民SEO担当者の今後の発展方向に関する提案

検索エンジンというプログラムを毎日扱う職業があります。このプログラムを満足させるために、彼らは毎日疑...

Shi Xiaolong: 初心者が入札を通じてお金を稼ぐ方法をすぐに学ぶにはどうすればいいでしょうか?

入札操作は自慢ではなく、あなた自身の実践経験に依存します。春節前にA5に「ウェブマスターが損をする4...

ロングテールキーワードのタイトルを最適化する7つの個人的な経験

ロングテール キーワードがウェブサイトの最適化に含まれていないのではないかと心配していませんか? 退...

SEO業界もモバイルインターネット時代へ移行する

2012 年が到来しました。世界の終わりが現実かどうかは関係なく、私たちは自分のやりたいことをして、...

ウェブブックマークはもはやSEOの重みを転送する効果を持たない

主なオンラインブックマークには、楽首、喜望峰、QQブックマーク、百度コレクション、アンソロジー、宝箱...

あなたのウェブサイト上の権威を失う原因となる可能性のあるスパムコンテンツを見つける

新年には、検索エンジンはスパムコンテンツの処理と判断においてますます正確になり、その処理は非常に厳格...

hostvenom - $39/E3-1230v2/8g メモリ/500g ハードディスク/5IP/IPMI/安定したコンピュータ ルーム

Hostvenom は別の情報を公開しました。E3 の Little Chicken はシカゴのデー...