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億ドルになる見通し

推薦する

SEOの考え方

Baidu は何度も変更を重ねており、SEO はますます難しくなっています。多くの人が「2013 年...

ビジネスを台無しにする可能性のあるクラウド コンピューティングの 10 の間違い

小さなミスが企業のクラウド コンピューティング戦略を台無しにしてしまう可能性があることが判明しました...

住宅改修製品のオンラインショッピングにおけるボトルネックの簡単な分析

生活には五大物があります。食品、衣料、住宅、交通、旅行です。食品から自動車、衣料から家電まで、電子商...

アリババクラウドの神龍アーキテクチャが世界有数のインターネット科学技術成果の一つに選ばれました

11月23日、烏鎮で開催された世界インターネット大会およびインターネット発展フォーラムにおいて、アリ...

深センガスとテンセントはコラボレーションプラットフォームをアップグレードし、7,500人の従業員をカバー

深センのスマートガス建設は段階的に進展している。 1月12日、深センガスとテンセントはスマートコラボ...

SEO クライアントからの苦情を減らすにはどうすればよいでしょうか?

SEO の仕事は血と涙を伴う厳しい仕事だという人もいます。王世凡はこの意見に大いに賛同しており、中国...

VMware 仮想化環境でのソフトウェア定義ストレージの使用

同社の現在の環境では、VMware 仮想化環境がアプリケーション システムの約 80% をホストして...

私はOpenStackに1~8年間携わってきました。ABCからHI、KOまで

ABC、HI、KO における OpenStack の経験2010 年末、通信グレードのサポート プラ...

Emissary Ingress を OPA と統合する方法

翻訳者 |李睿校正:孫淑娟API ゲートウェイは、マイクロサービスを公開する上で重要な役割を果たしま...

SEO にはコツはありません。努力によって達成されます。

最近新しいウェブサイトを始めたのですが、過去の最も苦しい日々に戻ってしまったようです。毎日編集したり...

グーグルの人事混乱続く:幹部がクラウドコンピューティング事業から撤退、前進困難

国際的に有数のテクノロジー企業であるグーグルで最近、上級幹部が相次いで退社しているのは、いささか驚き...

V.PS: シンガポール VPS がリリース (高性能/大帯域幅/中国最適化)、複数の割引、先行販売イベント「このサイト限定でトラフィック 2 倍」

v.ps(XTOMデータセンター傘下のクラウドサーバー)は現在、新製品シリーズであるシンガポールVP...

2コア8Gクラウドホストから、4大クラウドベンダーのオープニングプロモーションのうちどれが一番お手頃か見てみましょう!

2021年2月25日、JD Cloudが先陣を切って「新年ショッピングシーズン」プロモーションを開始...

行き詰まりを打破: F5 はどのようにして究極のアプリケーション保護、配信、最適化を実現するのでしょうか?

「 7年前に私がF5に入社することを決めたときを振り返ると、顧客やパートナーはそれを無謀な決断だと思...

frontrangehosting-ブラックフライデー、すべてのVPS(KVM/OPENVZ)が50%オフ

ブラック フライデーが近づいています。frontrangehosting のすべての VPS が 5...