The Java EE 7 Tutorialの51 Transactionsの章をテキトーに訳した。
51 Transactions
一般的なエンタープライズアプリケーションは一つ以上のデータベースに情報の保存と取得を行います。この情報は業務に必須なものなので、情報には正確かつリアルタイム性と信頼性が求められます。データの整合性が低下する場合があり、複数のプログラムで同時に同じ情報を更新可能になっていたり、システムエラーが処理中のビジネストランザクションで影響を与えたデータを残したまま部分的にだけデータを更新したり、といった場合です。こうしたシナリオを防ぐために、ソフトウェアトランザクションはデータの整合性を保証します。トランザクションは複数のプログラムによるコンカレントなデータアクセスを制御します。システムエラーの際には、トランザクションはリカバリ後にデータが一貫性を保つことを保証します。
この章では以降のトピックを扱います。
- Transactions in Java EE Applications
- What Is a Transaction?
- Container-Managed Transactions
- Bean-Managed Transactions
- Transaction Timeouts
- Updating Multiple Databases
- Transactions in Web Components
- Further Information about Transactions
51.1 Transactions in Java EE Applications
Java EEアプリケーションでは、トランザクションとは、ある一連のアクションがすべて正常に完了するか、各アクションのすべての変更が取り消されるか、のどちらか一方にしかなりません。トランザクションはコミットかロールバックのどちらかで終了します。
Java Transaction API (JTA)は、特定の実装から独立した方法でアプリケーションがトランザクションにアクセスする方法を提供します。JTAは、トランザクションマネージャと分散トランザクションシステムの間をつなぐ標準的なJavaインタフェースを定義しています。分散トランザクションシステムに含まれるものとは、トランザクションアプリケーション・Java EEサーバー・トランザクションが影響を与える共有リソースへのアクセスを制御するマネージャ、などです。
JTAはUserTransaction
インタフェースを定義しており、アプリケーションはこれを利用してトランザクションの開始・コミット・ロールバックを行います。アプリケーションコンポーネントは、JNDIルックアップjava:comp/UserTransaction
経由でUserTransaction
オブジェクトを取得するか、UserTransaction
オブジェクトのインジェクションを要求します。アプリケーションサーバはトランザクションマネージャと通信するためにJTAが定義するインタフェースの多くを使用します。トランザクションマネージャはリソースマネージャと通信するためにJTAが定義するインタフェースを使用します。
51.2 What Is a Transaction?
ビジネストランザクションをエミュレートするために、あるプログラムがいくつかのステップを実行すると仮定します。銀行口座のプログラムは、例えば、以下の疑似コードのように複数ステップを使用して当座預金から普通預金へ資金移動を行います。
begin transaction debit checking account credit savings account update history log commit transaction
この三ステップはすべて完了するか、ひとつも実行されない必要があります。そうでないと、データの整合性は失われます。トランザクション内のステップは全体として一つなので、トランザクション(transaction)はこれ以上分割できない作業単位として定義されます。
トランザクションは、コミットかロールバックか、二種類の方法で終了可能です。トランザクションがコミットするとき、データ修正はそのステートメントが保存したもので行われます。トランザクション内のステートメントが失敗する場合、トランザクションはロールバックし、トランザクション内のすてべのステートメントの影響は取り消されます。上記の疑似コードでは、もしディスクドライブがcredit
ステップでクラッシュした場合、トランザクションはロールバックしてdebit
ステートメントの更新データを取り消します。トランザクションが失敗したとしても、口座のバランスは取れているのでデータの整合性は保たれます。
上記の疑似コードでは、begin
とcommit
ステートメントがトランザクション境界を示しています。エンタープライズビーンを設計する場合、境界をコンテナマネージドかビーンマネージドのどちらの方法で設定するかを決める必要があります。
51.3 Container-Managed Transactions
container-managed transaction demarcationでエンタープライズビーンを使用する場合、EJBコンテナがトランザクション境界を設定します。セッションやメッセージドリブンなど、任意のエンタープライズビーンの種類でコンテナマネージドトランザクションを使用可能です。エンタープライズビーンのコードは明示的にトランザクション境界を設定しないので、コンテナマネージドトランザクションの開発は単純化されます。そのコードにはトランザクションの開始や終了を行うステートメントは含まれません。デフォルトでは、もしトランザクション境界を指定しない場合、エンタープライズビーンはコンテナマネージドトランザクション境界を使用します。
一般的に、コンテナはエンタープライズビーンのメソッドが開始する前に直ちにトランザクションを開始し、メソッドが終了する直前にトランザクションをコミットします。一つ一つのメソッドは単一のトランザクションに関連付けが可能です。ネストもしくは複数のトランザクションはメソッド内で許可されません。
コンテナマネージドトランザクションはすべてのメソッドがトランザクションに関連付けされることを要求しません。ビーン開発時には、ビーンメソッドとトランザクションの関連付けを指定するトランザクション属性を設定可能です。
コンテナマネージドトランザクション境界を使用するエンタープライズビーンは、コンテナのトランザクション境界に干渉するトランザクション管理メソッドを使用してはいけません。そうしたメソッドの例としては、java.sql.Connection
のcommit
, setAutoCommit
, rollback
メソッドや、javax.jms.Session
のcommit
とrollback
などです。もしトランザクション境界を超える制御をしたい場合、アプリケーションマネージドトランザクション境界を使用する必要があります。
また、コンテナマネージドトランザクション境界を使用するエンタープライズビーンはjavax.transaction.UserTransaction
インタフェースを使用してはいけません。
51.3.1 Transaction Attributes
トランザクション属性(transaction attribute)はトランザクションのスコープを制御します。図 51-1は、なぜスコープ制御が重要なのかを図示しています。図では、method-A
がトランザクションを開始し、Bean-2
のmethod-B
を呼び出しています。method-B
を実行するとき、method-A
が開始したトランザクションスコープ内なのか、それとも新規のトランザクションなのか、どちらになるのでしょうか? その答えはmethod-B
のトランザクション属性に依存します。
図 51-1 トランザクションスコープ
Description of "Figure 51-1 Transaction Scope"
トランザクション属性は以下の値のうち一つを取ります。
Required
RequiresNew
Mandatory
NotSupported
Supports
Never
51.3.1.1 Required Attribute
もしクライアントがトランザクション内で動作していてエンタープライズビーンのメソッドを呼び出す場合、そのメソッドはクライアントのトランザクション内で実行されます。クライアントがトランザクションに関連付けされていない場合、コンテナはメソッド実行前に新規のトランザクションを開始します。
Required
属性は、すべてのエンタープライズビーンのメソッドをコンテナマネージドトランザクション境界で動作させるための、暗黙的なトランザクション属性です。一般的に、他のトランザクション属性をオーバーライドする必要がある場合以外、Required
属性を設定することはありません。トランザクション属性は宣言的なので、これをあとで変更することは簡単です。
51.3.1.2 RequiresNew Attribute
もしクライアントがトランザクション内で動作していてエンタープライズビーンのメソッドを呼び出す場合、コンテナは以下のステップを取ります。
クライアントがトランザクションに関連付けされていない場合、コンテナはメソッド実行前に新規のトランザクションを開始します。
RequiresNew
属性を使うべきケースは、あるメソッドが常に新規トランザクションで実行されることを保証したい場合です。
51.3.1.3 Mandatory Attribute
もしクライアントがトランザクション内で動作していてエンタープライズビーンのメソッドを呼び出す場合、そのメソッドはクライアントのトランザクション内で実行されます。クライアントがトランザクションに関連付けされていない場合、コンテナはTransactionRequiredException
をスローします。
エンタープライズビーンのメソッドがクライアントのトランザクションを使用しなければならない場合にMandatory
属性を使用します。
51.3.1.4 NotSupported Attribute
もしクライアントがトランザクション内で動作していてエンタープライズビーンのメソッドを呼び出す場合、コンテナはメソッド実行前にクライアントのトランザクションをサスペンドします。メソッド完了後、コンテナはクライアントのトランザクションを復元します。
クライアントがトランザクションに関連付けされていない場合、コンテナはメソッド実行前に新規トランザクションを開始しません。
NotSupported
属性はメソッドがトランザクションを必要としない場合に使用します。トランザクションにはオーバーヘッドがあるので、この属性はパフォーマンスの改善に使います。
51.3.1.5 Supports Attribute
もしクライアントがトランザクション内で動作していてエンタープライズビーンのメソッドを呼び出す場合、このメソッドはクライアントのトランザクション内で実行されます。クライアントがトランザクションに関連付けされていない場合、コンテナはメソッド実行前に新規トランザクションを開始しません。
メソッドのトランザクションの振る舞いが変化する可能性がある場合、用心のためにSupports
属性を使うべきです。
51.3.1.6 Never Attribute
もしクライアントがトランザクション内で動作していてエンタープライズビーンのメソッドを呼び出す場合、コンテナはRemoteException
をスローします。クライアントがトランザクションに関連付けされていない場合、コンテナはメソッド実行前に新規トランザクションを開始しません。
51.3.1.7 Summary of Transaction Attributes
表 51-1はトランザクション属性の影響をまとめたものです。T1
とT2
トランザクションはコンテナ制御下です。T1
トランザクションは、エンタープライズビーンのメソッドを呼び出すクライアントに関連付けられています。多くの場合、クライアントとは別のエンタープライズビーンです。T2
トランザクションはメソッド実行前にコンテナが開始するものです。
表 51-1の最後の列の"なし"は、ビジネスメソッドがコンテナ制御下のトランザクション内で実行されないことを意味します。しかし、そうしたビジネスメソッドのデータベース呼び出しは、データベース管理システムのトランザクションマネージャーの制御化にあることが多いです。
表 51-1 トランザクション属性とスコープ
トランザクション属性 | クライアントのトランザクション | ビジネスメソッドのトランザクション |
---|---|---|
Required |
なし |
T2 |
Required |
T1 |
T1 |
RequiresNew |
なし |
T2 |
RequiresNew |
T1 |
T2 |
Mandatory |
なし |
エラー |
Mandatory |
T1 |
T1 |
NotSupported |
なし |
なし |
NotSupported |
T1 |
なし |
Supports |
なし |
なし |
Supports |
T1 |
T1 |
Never |
なし |
なし |
Never |
T1 |
エラー |
51.3.1.8 Setting Transaction Attributes
トランザクション属性の指定は、エンタープライズビーンクラスかメソッドをjavax.ejb.TransactionAttribute
アノテーションを付与することで行い、アノテーションにはjavax.ejb.TransactionAttributeType
定数値の一つを設定します。
エンタープライズビーンクラスに@TransactionAttribute
を付与する場合、指定されたTransactionAttributeType
はそのクラスの全ビジネスメソッドに適用されます。@TransactionAttribute
をビジネスメソッドに付与する場合、TransactionAttributeType
はメソッドにのみ適用されます。@TransactionAttribute
アノテーションがクラスとメソッド両方に付与されている場合、メソッドのTransactionAttributeType
がクラスのTransactionAttributeType
をオーバーライドします。
表 51-2に示すTransactionAttributeType
定数値がこのセクションで上述したトランザクション属性に対応しています。
表 51-2 TransactionAttributeType定数値
トランザクション属性 | TransactionAttributeType定数値 |
---|---|
Required |
TransactionAttributeType.REQUIRED |
RequiresNew |
TransactionAttributeType.REQUIRES_NEW |
Mandatory |
TransactionAttributeType.MANDATORY |
NotSupported |
TransactionAttributeType.NOT_SUPPORTED |
Supports |
TransactionAttributeType.SUPPORTS |
Never |
TransactionAttributeType.NEVER |
以下のコードは@TransactionAttribute
アノテーションの使用方法の一例です。
@TransactionAttribute(NOT_SUPPORTED) @Stateful public class TransactionBean implements Transaction { ... @TransactionAttribute(REQUIRES_NEW) public void firstMethod() {...} @TransactionAttribute(REQUIRED) public void secondMethod() {...} public void thirdMethod() {...} public void fourthMethod() {...} }
この例では、TransactionBean
クラスのトランザクション属性にはNotSupported
、firstMethod
にはRequiresNew
、secondMethod
にはRequired
が設定されています。メソッドに付与された@TransactionAttribute
はクラスの@TransactionAttribute
をオーバーライドするので、firstMethod
の呼び出しは新規トランザクションを生成し、secondMethod
の呼び出しは現在のトランザクション下で実行するか新規のトランザクションを開始するかのどちらかになります。thirdMethod
やfourthMethod
はトランザクション下には入りません。
51.3.2 Rolling Back a Container-Managed Transaction
コンテナマネージドトランザクションをロールバックする方法は二通りあります。一つ目は、もしシステム例外がスローされる場合、コンテナは自動的にトランザクションをロールバックします。二つ目は、EJBContext
インターフェースのsetRollbackOnly
メソッドを実行することで、ビーンのメソッドはトランザクションをロールバックするようコンテナに指示できます。もしビーンがアプリケーション例外をスローする場合、自動的なロールバックはされませんが、setRollbackOnly
を呼ぶことでロールバックが可能です。
51.3.3 Synchronizing a Session Bean's Instance Variables
オプションのSessionSynchronization
は、セッションビーンのインスタンスがトランザクション同期化通知(transaction synchronization notifications)を受信可能にします。たとえば、エンタープライズビーンのインスタンス変数とそれに対応するデータベースの値を同期化させることができます。コンテナはトランザクションの主要な段階ごとにSessionSynchronization
のメソッド(afterBegin
, beforeCompletion
, afterCompletion
)を呼び出します。
afterBegin
メソッドは新規トランザクションが開始したことをインスタンスに知らせます。コンテナはビジネスメソッド実行直前にafterBegin
を呼び出します。
コンテナはビジネスメソッド終了後かつトランザクションコミット直前にbeforeCompletion
メソッドを呼び出します。beforeCompletion
メソッドはセッションビーンがロールバック(をsetRollbackOnly
を呼び出すことで)できる最後のタイミングです。
afterCompletion
メソッドはトランザクションが完了したことを意味します。このメソッドはboolean
の単一引数を持ち、その値がtrue
ならトランザクションはコミットし、false
ならロールバックしています。
51.3.4 Methods Not Allowed in Container-Managed Transactions
コンテナが設定するトランザクション境界に干渉するメソッドは使用すべきではありません。以下のメソッドが禁止されています。
java.sql.Connection
のcommit
,setAutoCommit
,rollback
メソッド。javax.ejb.EJBContext
のgetUserTransaction
メソッド。javax.transaction.UserTransaction
のメソッド。
だたし、アプリケーションマネージドトランザクション境界でならこれらのメソッドは使用可能です。
51.4 Bean-Managed Transactions
ビーンマネージドトランザクション境界(bean-managed transaction demarcation)では、セッションやメッセージドリブンビーンのコードが明示的にトランザクション境界を設定します。コンテナマネージドトランザクションはコード量を少なくできますが、制限があります。メソッドを実行するとき、単一トランザクションを関連付けるか全くトランザクションを作らないかのどちらかになります。もしこの制限が自前のビーン作成を困難にするのなら、ビーンマネージドトランザクションの使用を考慮すべきです。
以下の疑似コードは、アプリケーションマネージドトランザクションで取得可能な細かいレベルの制御を示しています。各種の状態をチェックすることで、疑似コードがビジネスメソッド内でトランザクションを開始か終了するかを決定します。
begin transaction ... update table-a ... if (condition-x) commit transaction else if (condition-y) update table-b commit transaction else rollback transaction begin transaction update table-c commit transaction
セッションやメッセージドリブンビーンでアプリケーションマネージドトランザクションをコーディングする場合、Java Database ConnectivityかJTAトランザクションのどちらを使用するかを選ぶ必要があります。
51.4.1 JTA Transactions
JTAやJava Transaction APIによって、トランザクションマネージャーの実装とは独立した方法でトランザクション境界を設定可能になります。GlassFish ServerはJava Transaction Service (JTS)のトランザクションマネージャーを実装しています。しかし、コードではJTSのメソッドを直接呼ばずに、代わりにJTAのメソッドを呼び出します。JTAはローレベルのJTSを呼び出します。
JTA transactionは、Java EEトランザクションマネージャが制御します。異なるベンダーの複数のデータベースにまたがる更新が可能なため、そうした場合にJTAトランザクションを使用可能です。特定のDBMSのトランザクションマネージャは異機種環境のデータベースでは動作しないことがあります。しかし、Java EEトランザクションマネージャは一つの制限があり、ネストされたトランザクションをサポートしません。つまり、インスタンスでは以前のトランザクションが終了するまでトランザクションを開始できない、という意味です。
JTAトランザクションを使用するには、javax.transaction.UserTransaction
インターフェースのbegin
, commit
, rollback
メソッドを呼び出します。
51.4.2 Returning without Committing
ビーンマネージドトランザクションを使用するステートレスセッションビーンでは、ビジネスメソッドはreturnする前にコミットかロールバックをしなければなりません。ただし、ステートフルセッションビーンではこの制約はありません。
JTAトランザクションを使用するステートフルセッションビーンでは、ビーンインスタンスとトランザクションの関連付けは複数のクライアント呼び出しにまたがって保持されます。クライアントがデータベースの接続と終了のビジネスメソッドを呼び出していたとしても、インスタンスがトランザクションを完了するまでは関連付けは保持されます。
JDBCトランザクションを使用するステートフルセッションビーンでは、JDBCコネクションは複数の呼び出しにまたがってビーンインスタンスとトランザクションの関連付けを保持します。コネクションがクローズされると、関連付けは解除されます。
51.4.3 Methods Not Allowed in Bean-Managed Transactions
ビーンマネージドトランザクションではEJBContext
インターフェースのgetRollbackOnly
とsetRollbackOnly
メソッドは呼び出してはいけません。これらのメソッドはコンテナマネージドトランザクションでのみ使用すべきです。ビーンマネージドトランザクションでは、UserTransaction
インターフェースのgetStatus
とrollback
を呼び出します。
51.5 Transaction Timeouts
コンテナマネージドトランザクションでは、Administration Consoleでトランザクションのタイムアウト感覚を設定可能です。Starting the Administration Consoleを参照してください。
ビーンマネージドJTAトランザクションを使用するエンタープライズビーンでは、UserTransaction
インターフェースのsetTransactionTimeout
メソッドを呼び出します。
51.5.1 To Set a Transaction Timeout
- Administration Consoleで、Configurationsの次にserver-configを開き、Transaction Serviceを選択する。
- Transaction Serviceページで、Transaction Timeoutフィールド値に任意の値(例えば5)を設定する。
この設定では、トランザクションが5秒以内に完了しないとEJBコンテナがそのトランザクションをロールバックします。
デフォルト値は0で、トランザクションはタイムアウトしないことを意味します。 - Saveをクリックする。
51.6 Updating Multiple Databases
Java EEトランザクションマネージャは、ビーンマネージドJDBCトランザクションを除く、すべてのエンタープライズビーンのトランザクションを制御します。Java EEトランザクションマネージャによって、エンタープライズビーンはトランザクション内で複数のデータベースを更新できます。図 51-2と図 51-3に単一トランザクションで複数データベースを更新する二つのシナリオを示します。
図 51-2では、クライアントはBean-A
でビジネスメソッドを呼び出します。ビジネスメソッドはトランザクションを開始し、データべースXを更新し、データベースYを更新し、Bean-B
のビジネスメソッドを呼び出します。二つ目のビジネスメソッドはデータベースZを更新し、Bean-A
のビジネスメソッドに制御を返し、トランザクションをコミットします。全部で三つのデータベース更新が同一トランザクションで発生しています。
図 51-3では、クライアントはBean-A
のビジネスメソッドを呼び出し、トランザクションを開始してデータベースXを更新します。それから、Bean-A
はリモートJava EEサーバのBean-B
のメソッドを呼び出します。Bean-B
のメソッドはデータベースYを更新します。Java EEサーバのトランザクションマネージャは同一トランザクションで二つのデータベースが更新されることを保証します。
図 51-2 複数データベースの更新
図 51-3 Java EEサーバをまたぐ複数データベースの更新
51.7 Transactions in Web Components
java.sql.Connection
もしくはjavax.transaction.UserTransaction
のどちらかを使用してwebコンポーネントでトランザクションを使用可能です。これらはビーンマネージドトランザクションのセッションビーンが使用可能なインターフェースと同じです。UserTransaction
インタフェースを使用するトランザクションについてはJTA Transactionsを参照してください。
51.8 Further Information about Transactions
トランザクションに関するより詳細な情報については、Java Transaction API 1.2仕様 https://www.jcp.org/en/jsr/detail?id=907 を参照してください。
関連リンク
- The Java EE 7 Tutorialのテキトー翻訳まとめ - Qiita - Java EE 7 Tutorialのうち、自分がテキトー翻訳したものの一覧