Spring Boot DB連携

Oracle上で@Transactionalアノテーションをネストして利用してみた

@Transactionalアノテーションはクラス間でネストして利用することができ、propagation属性の設定値を変えることで、今まであるトランザクションを引き継いだり、常に新しいトランザクションを開始したりすることができる。

今回は、@Transactionalアノテーションをクラス間でネストして設定することで、トランザクションのコミット/ロールバックの動作を試してみたので、その結果を共有する。

なお、@Transactionalアノテーションに指定するpropagation属性の設定値については、以下のサイトを参照のこと。
https://qiita.com/NagaokaKenichi/items/a279857cc2d22a35d0dd#propagation

前提条件

下記記事の実装が完了していること。

Oracle上でSpring Bootの@Transactionalアノテーションの挙動を調べてみたSpring Bootを利用したアプリケーションでDB接続を利用する際、@Transactionalアノテーションをつけたメソッド内でD...

サンプルプログラムの作成

作成したサンプルプログラムの構成は以下の通り。
サンプルプログラムの構成
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。

UserDataMapper.java、UserDataMapper.xmlの内容は以下の通りで、findByIdListメソッドにより、指定したIDリストのIDにあてはまるユーザーデータリストを取得する処理を追加している。

また、サービスクラスの内容は以下の通りで、@Transactionalアノテーションを付与しているサブクラスの各メソッドの呼び出しを行っている。

さらに、サービスクラスのサブクラスの内容は以下の通りで、@Transactionalアノテーションのpropagation属性の設定値を2パターンで設定している。

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-oracle-transactional-nest/demo



「CODE×CODE」は、需要の高い技術(AWS, Python等)を習得できるプログラミングスクールスクールだった近年、さまざまな会社でクラウド(特にIaaSやPaaSのパブリッククラウド)の需要が非常に高まっていて、クラウドサービスによるシステム開...

サンプルプログラムの実行結果(DemoServiceクラスで「@Transactional」アノテーションあり)

DemoServiceクラスのtransUserDataメソッドの「@Transactional」アノテーションを有効にした場合の実行結果は、以下の通り。

1) 以下のように、DemoServiceクラスのtransUserDataメソッドの「@Transactional」アノテーションを有効にする。
サンプルプログラムの実行結果_1_1

2) id=4, id=8, id=10のデータが全て更新成功する状態にした場合の、サンプルプログラムの実行結果は、以下の通り。

2-1) Spring Bootのメインクラスを実行する前のデータは、以下の通り。
サンプルプログラムの実行結果_1_2_1

2-2) id=4, id=8, id=10のデータが全て更新成功する状態にして、Spring Bootのメインクラス(DemoApplication.java)を実行する。以下の赤枠は、id=4のデータを更新成功する状態に設定している状態を示している。
サンプルプログラムの実行結果_1_2_2

2-3) コンソールログに出力される内容は、以下の通り。
サンプルプログラムの実行結果_1_2_3

2-4) Spring Bootのメインクラスを実行した後のデータは、以下の通りで、全てのデータのNAMEが更新されていることが確認できる。
サンプルプログラムの実行結果_1_2_4

3) id=4のデータが更新失敗する状態にした場合の、サンプルプログラムの実行結果は、以下の通り。なお、id=4は、DemoServiceSubクラスで、「@Transactional」アノテーションでpropagation属性の指定なし(今まであるトランザクションを引き継ぐ)の状態になっている。

3-1) Spring Bootのメインクラスを実行する前のデータは、以下の通り。
サンプルプログラムの実行結果_1_3_1

3-2) id=4のデータが更新失敗する状態にして、Spring Bootのメインクラス(DemoApplication.java)を実行する。
サンプルプログラムの実行結果_1_3_2

3-3) コンソールログに出力される内容は、以下の通り。
サンプルプログラムの実行結果_1_3_3

3-4) Spring Bootのメインクラスを実行した後のデータは、以下の通りで、「@Transactional」アノテーションでpropagation属性の指定のないメソッドで実行した更新処理は、全てロールバックされることが確認できる。
サンプルプログラムの実行結果_1_3_4

4) id=8のデータが更新失敗する状態にした場合の、サンプルプログラムの実行結果は、以下の通り。なお、id=8は、DemoServiceSubクラスで、「@Transactional」アノテーションでpropagation属性がREQUIRES_NEW(常に新しいトランザクションを開始する)の状態になっている。

4-1) Spring Bootのメインクラスを実行する前のデータは、以下の通り。
サンプルプログラムの実行結果_1_4_1

4-2) id=8のデータが更新失敗する状態にして、Spring Bootのメインクラス(DemoApplication.java)を実行する。
サンプルプログラムの実行結果_1_4_2

4-3) コンソールログに出力される内容は、以下の通り。
サンプルプログラムの実行結果_1_4_3

4-4) Spring Boottのメインクラスを実行した後のデータは、以下の通りで、id=7,id=8のデータを更新しているトランザクションのみがロールバックされることが確認できる。
サンプルプログラムの実行結果_1_4_4

5) id=10のデータが更新失敗する状態にした場合の、サンプルプログラムの実行結果は、以下の通り。

5-1) Spring Bootのメインクラスを実行する前のデータは、以下の通り。
サンプルプログラムの実行結果_1_5_1

5-2) id=10のデータが更新失敗する状態にして、Spring Bootのメインクラス(DemoApplication.java)を実行する。
サンプルプログラムの実行結果_1_5_2

5-3) コンソールログに出力される内容は、以下の通り。
サンプルプログラムの実行結果_1_5_3

5-4) Spring Bootのメインクラスを実行した後のデータは、以下の通りで、「@Transactional」アノテーションでpropagation属性の指定のないメソッドで実行した更新処理は、全てロールバックされることが確認できる。
サンプルプログラムの実行結果_1_5_4



「EaseUS Todo Backup」は様々な形でバックアップ取得が行える便利ツールだったパソコン内のデータを、ファイル/パーティション/ディスク等の様々な単位でバックアップしたり、バックアップのスケジュール設定や暗号化設定も...

サンプルプログラムの実行結果(DemoServiceクラスで「@Transactional」アノテーションなし)

DemoServiceクラスのtransUserDataメソッドの「@Transactional」アノテーションを無効にした場合の実行結果は、以下の通り。

1) 以下のように、DemoServiceクラスのtransUserDataメソッドの「@Transactional」アノテーションを無効にする。
サンプルプログラムの実行結果_2_1

2) id=4, id=8, id=10のデータが全て更新成功する状態にした場合の、サンプルプログラムの実行結果は、以下の通り。

2-1) Spring Bootのメインクラスを実行する前のデータは、以下の通り。
サンプルプログラムの実行結果_2_2_1

2-2) id=4, id=8, id=10のデータが全て更新成功する状態にして、Spring Bootのメインクラス(DemoApplication.java)を実行する。以下の赤枠は、id=4のデータを更新成功する状態に設定している状態を示している。
サンプルプログラムの実行結果_2_2_2

2-3) コンソールログに出力される内容は、以下の通り。
サンプルプログラムの実行結果_2_2_3

2-4) Spring Bootのメインクラスを実行した後のデータは、以下の通りで、全てのデータのNAMEが更新されていることが確認できる。
サンプルプログラムの実行結果_2_2_4

3) id=4のデータが更新失敗する状態にした場合の、サンプルプログラムの実行結果は、以下の通り。なお、id=4は、DemoServiceSubクラスで、「@Transactional」アノテーションでpropagation属性の指定なし(今まであるトランザクションを引き継ぐ)の状態になっている。

3-1) Spring Bootのメインクラスを実行する前のデータは、以下の通り。
サンプルプログラムの実行結果_2_3_1

3-2) id=4のデータが更新失敗する状態にして、Spring Bootのメインクラス(DemoApplication.java)を実行する。
サンプルプログラムの実行結果_2_3_2

3-3) コンソールログに出力される内容は、以下の通り。
サンプルプログラムの実行結果_2_3_3

3-4) Spring Bootのメインクラスを実行した後のデータは、以下の通りで、id=3,id=4のデータを更新しているトランザクションのみがロールバックされることが確認できる。
サンプルプログラムの実行結果_2_3_4

4) id=8のデータが更新失敗する状態にした場合の、サンプルプログラムの実行結果は、以下の通り。なお、id=8は、DemoServiceSubクラスで、「@Transactional」アノテーションでpropagation属性がREQUIRES_NEW(常に新しいトランザクションを開始する)の状態になっている。

4-1) Spring Bootのメインクラスを実行する前のデータは、以下の通り。
サンプルプログラムの実行結果_2_4_1

4-2) id=8のデータが更新失敗する状態にして、Spring Bootのメインクラス(DemoApplication.java)を実行する。
サンプルプログラムの実行結果_2_4_2

4-3) コンソールログに出力される内容は、以下の通り。
サンプルプログラムの実行結果_2_4_3

4-4) Spring Bootのメインクラスを実行した後のデータは、以下の通りで、id=7,id=8のデータを更新しているトランザクションのみがロールバックされることが確認できる。
サンプルプログラムの実行結果_2_4_4

5) id=10のデータが更新失敗する状態にした場合の、サンプルプログラムの実行結果は、以下の通り。なお、id=10は、DemoServiceクラスのtransUserDataメソッドで直接、DB更新をするようになっている。

5-1) Spring Bootのメインクラスを実行する前のデータは、以下の通り。
サンプルプログラムの実行結果_2_5_1

5-2) id=10のデータが更新失敗する状態にして、Spring Bootのメインクラス(DemoApplication.java)を実行する。
サンプルプログラムの実行結果_2_5_2

5-3) コンソールログに出力される内容は、以下の通り。
サンプルプログラムの実行結果_2_5_3

5-4) Spring Bootのメインクラスを実行した後のデータは、以下の通りで、更新に失敗したid=10のデータ以外はコミットされていることが確認できる。
サンプルプログラムの実行結果_2_5_4

要点まとめ

  • @Transactionalアノテーションで、propagation属性の指定が無い場合は、トランザクションが開始されていなければ新規に開始し、すでに開始されていればそのトランザクションをそのまま利用する仕様となる。
  • @Transactionalアノテーションで、propagation属性にREQUIRES_NEWを指定した場合は、常に新しいトランザクションを開始し、トランザクションが存在する場合は中断して新しいトランザクションを開始する仕様となる。