1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SpringBootにおけるトランザクション「@Transactional」の動きをシンプルに理解する

Last updated at Posted at 2025-01-13

こんにちは。SpringBootの勉強シリーズです。

今回はSpringBootのアノテーション「@Transactional」の挙動を整理します。

なお、トランザクション分離レベルなどは触れません。トランザクションのシンプルな挙動の理解を目指します。

環境

  • SpringBoot(バージョンは適当)
  • Postgresql(バージョンは適当)

※たぶんバージョンとかDBに関係あることは書きません。

まずはじめに

あいかわらずSpringBootの勉強中です。

よく以下のような実装を見ます。今日はこの実装を紐解いていきます。
(実際のソースコードは10倍くらい複雑ですけど)

    @Transactional
    public void insertSample(String name, int point) {
        sampleMapper.insertSample(name, point);
    }

トランザクションとは

複数の処理をまとめた単位らしいです。

トランザクションの挙動

以下のような処理があります。

    @Transactional
    public void insertSample(String name, int point) {
        sampleMapper.insertSample(name, point); // ①DBへの登録
        doSomething(); // ②ここでエラーが発生するかもしれない
    }

正常終了時

@Transactionalを付与されたメソッドが完了時に、DBに対してコミットして、更新を実際に反映します。

異常終了時

①で登録後に、②でエラーが発生するとします。

この場合に、トランザクションとして、エラーを検知してロールバックして、①のINSERTがなかったことになります。

以下のように、トランザクション内でエラーが起こった場合に、ロールバックが発生します。

何が嬉しいのか

以下のような場合にメリットがあります。

  • 処理取り消し(ロールバック)の個別実装が不要
  • 複数テーブルに跨った更新に対して、一貫性を担保できる
  • Javaの領域で起こったエラーに対して、DBのロールバックをフレームワークに任せることができる

たとえば下記の図のように、2つのテーブルを更新する処理があるとします。この場合、片方のテーブルだけが更新されて、もう片方が更新されないという状況を防ぐことができます。トランザクションを使用することで、両方のテーブルの更新が成功するか、両方とも元の状態に戻るかのどちらかになります。

トランザクションの処理中のDBの状態は?

コミットされていなければ、DBに反映されません。

すなわち、処理中のトランザクションは、コミットされるまで他者からは反映を確認できません。

以下の例ですと、ユーザーBからは、ユーザーAの処理がコミットされるまでは、変更前の情報が参照されます。

ではどんな時でも、トランザクションにしておけば安心なのか

そういうわけではありません。トランザクション内での更新は、そのトランザクションがコミットされるまでレコードをロックするため、他からの更新が待機してしまいます。

これは以下のような状況で問題になる可能性があります:

  • 大量のデータを更新する処理
  • 外部APIを呼び出すなど、処理時間が長くなる可能性がある処理
  • 複数のトランザクションが同じテーブルに対して更新を行う処理

特に注意が必要なのは、複数のトランザクションが異なる順序でテーブルを更新する場合です。
以下のような状況では、デッドロックが発生する可能性があります。

実際には、デッドロックが発生した場合、各DBMSによって検知してエラーとなります。そして場合によりますが、どちらかのトランザクションをエラーとし、どちらかを完了させます。

このような問題を防ぐためには、以下のようなことをします。

  1. トランザクションの範囲は必要最小限にする

    • 外部APIの呼び出しはトランザクションの外で行う
    • 大量データの更新は適切な単位で分割する
  2. テーブルの更新順序を統一する

    • 複数のトランザクションで同じテーブルを更新する場合は、更新順序をアプリケーション側で揃える
    • 例:テーブルA → テーブルB → テーブルC という順序を統一する

トランザクションは便利な機能ですが、適切な範囲で使用することが重要です。
必要以上に大きな範囲をトランザクションにすると、アプリケーションのパフォーマンスに影響を与える可能性があります。

まとめ

SpringBootにおいて、トランザクション(@Transactional)配下の処理が

  • 成功した場合
    • DBにも反映される
  • 失敗した場合
    • DBもロールバックする

という挙動でACID特性のA(Atomicity)が担保されます。

実際には

  • フレームワークの挙動(アノテーションのその他のオプション)
  • DBの挙動
  • トランザクション分離レベル

などなど、理解すべきことが多いですが、まずはシンプルに挙動をまとめてみました。

初学者の方、実際に案件で使用している方に、勉強も兼ねてご参考になれば幸いです。

以上です。また何かまとめます。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?