コンテンツにスキップ

トランザクション管理方針⚓︎

トランザクション管理とは、トランザクションの ACID 特性を担保するための方式のことです。 システム内でトランザクション管理方針を定めることで、データ不整合やデッドロックの発生を防ぎ、処理性能を高めます。 ここでは、 AlesInfiny Maia OSS Edition における基本的なトランザクション管理方針を説明します。

トランザクションの管理単位⚓︎

オンライン処理におけるトランザクションの管理単位⚓︎

オンライン処理を行うアプリケーションでは、以下の方針でトランザクションを管理します。

  • トランザクションの単位をアプリケーションサービス内の各 public メソッドとする
  • データアクセスを含む処理を一律でトランザクション管理の対象とする

その他のトランザクションの管理単位

更新系のデータアクセスを含む処理のまとまりのみトランザクション制御する方針も取ることができます。つまり、参照系のデータアクセスのみ行う処理のまとまりはトランザクション制御しないということです。 この方針を採用する場合、トランザクション制御していない部分に更新系のデータアクセスが含まれていないことを十分に確認する必要があります。

バッチ処理におけるトランザクションの管理単位⚓︎

(今後追加予定)

トランザクションの実現方式⚓︎

Spring Framework が提供するトランザクション管理の実現方式は、大きく「宣言的トランザクション管理」と「プログラミングによるトランザクション管理」の2つに分けられます。 上述の「アプリケーションサービスの public メソッドを一律でトランザクション管理単位とする」という方針から、実装の容易さや業務ロジックとトランザクション制御処理の分離の観点から、「宣言的トランザクション」を採用することを推奨します。

宣言的トランザクション管理⚓︎

Spring Framework における宣言的トランザクション管理では、メソッドをトランザクション管理の単位として、トランザクションの設定を定義します。 詳細は 宣言的トランザクション管理 を参照してください。

設定方法は Xml による設定と @Transactional というアノテーションを付与する方法がありますが、 Spring Boot を利用したアプリケーションではアノテーションベースの設定が一般的です。

上述の方針に従い、トランザクション単位となるアプリケーションサービスのメソッドに対し、@Transactional アノテーションを付与します。 @Transactional アノテーションの設定については、@Transactional を使用する を参照してください。

以下では特に検討すべき設定のみ記載します。

  • propagation :トランザクションの伝播設定。特に他のアプリケーションサービスからも呼び出されるメソッドについて、トランザクションを引き継ぐのか、新しいトランザクションとするのかを設定する
  • rollbackFor/rollbackForClassName :設定された例外がトランザクション内で例外が発生した場合にロールバックする。デフォルト設定は RuntimeException であるため、非検査例外をロールバック対象としたい場合には設定が必要

プログラミングによるトランザクション管理⚓︎

プログラミングによるトランザクション管理とは、 Spring Framework が提供する機能を利用して、メソッド内でトランザクションの開始や終了(トランザクション範囲の指定)を明示的に実装する方法です。 詳細は プログラムによるトランザクション管理 を参照してください。

この方法のメリットは、プログラミング実装により、柔軟にトランザクション管理処理を行うことができることです。 しかし一方で、業務ロジックとトランザクション管理処理が入り組んだコードとなり保守性が低下しやすい、開発者により実装コードのばらつきが起きやすい、といったデメリットもあります。 柔軟性は高いものの、宣言的トランザクション管理でトランザクション管理に関する要件を十分に満たせることも多いです。 そのため基本的には宣言的トランザクション管理を採用し、プログラムによるトランザクション管理でないと要件を満たすことが難しい場合にのみ、採用を検討することを推奨します。

トランザクションのコミットとロールバック方針⚓︎

アプリケーションサービスのメソッド内の処理で、ハンドリングされない例外が発生したか否かで、更新内容をデータベースへコミットするか、ロールバックをするかを決定します。 例外が発生した場合でも、アプリケーションサービス内で例外処理が行われる場合には、それは正常系の処理としてロールバックしません。例外処理しつつ異常系の処理としてロールバックさせる場合には、例外処理の中で改めて例外を throw します。 最終的にアプリケーションサービスで発生した例外は、コントローラーまたは集約例外ハンドラーで処理されます。

トランザクション分離レベル⚓︎

既定値として ReadCommitted を選択します。 コミット済みデータのみを読み取るため、ダーティリードを防ぎます。 また、同一データに対して参照操作が行われても待ち時間が発生しないため、データアクセス処理時間を短縮できます。

非同期処理の考慮⚓︎

サーバーサイドの実装には非同期処理を利用することがしばしばあります。 非同期処理の実現方法にもよりますが、非同期処理においても適切にトランザクション管理がなされるように、注意する必要があります。 例えば Spring Framework の @Async アノテーションを利用した方法でもそうですが、基本的に非同期処理は呼び出し元とは別のトランザクションとなるため、その点を考慮する必要があります。

(非同期処理については追記予定)

更新競合の対策⚓︎

システムの処理性能を高めるため、原則として楽観的排他制御(楽観ロック)を採用します。 楽観ロックでは、更新競合の発生がまれであることを前提として更新対象データにロックをかけず、更新操作の実行時にデータの整合性が取れていることを確認します。 更新競合が発生した場合は例外が発生し、更新内容が取り消されます。

O/R Mapper として MyBatis を採用する場合、楽観ロックのための API は特に提供されていないため、独自実装する必要があります。 以下は実現方法の一例です。

  • 対象テーブルに楽観ロック用のカラムを追加する
  • 更新系のクエリについて、 WHERE 句に楽観ロック用カラムの更新チェックを加える
  • 更新系のクエリについて、更新時に楽観ロック用カラムを更新する

デッドロック対策⚓︎

単一のトランザクション内で複数テーブルへのアクセスがある場合、トランザクション間でアクセスする順序を統一することでデッドロックの発生を予防します。