読者です 読者をやめる 読者になる 読者になる

kagamihogeの日記

kagamihogeの日記です。

The Java EE 7 TutorialのTransactionsの章をテキトーに訳した

The Java EE 7 Tutorial51 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サーバー・トランザクションが影響を与える共有リソースへのアクセスを制御するマネージャ、などです。

JTAUserTransactionインタフェースを定義しており、アプリケーションはこれを利用してトランザクションの開始・コミット・ロールバックを行います。アプリケーションコンポーネントは、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ステートメントの更新データを取り消します。トランザクションが失敗したとしても、口座のバランスは取れているのでデータの整合性は保たれます。

上記の疑似コードでは、begincommitステートメントトランザクション境界を示しています。エンタープライズビーンを設計する場合、境界をコンテナマネージドかビーンマネージドのどちらの方法で設定するかを決める必要があります。

51.3 Container-Managed Transactions

container-managed transaction demarcationエンタープライズビーンを使用する場合、EJBコンテナがトランザクション境界を設定します。セッションやメッセージドリブンなど、任意のエンタープライズビーンの種類でコンテナマネージドトランザクションを使用可能です。エンタープライズビーンのコードは明示的にトランザクション境界を設定しないので、コンテナマネージドトランザクションの開発は単純化されます。そのコードにはトランザクションの開始や終了を行うステートメントは含まれません。デフォルトでは、もしトランザクション境界を指定しない場合、エンタープライズビーンはコンテナマネージドトランザクション境界を使用します。

一般的に、コンテナはエンタープライズビーンのメソッドが開始する前に直ちにトランザクションを開始し、メソッドが終了する直前にトランザクションをコミットします。一つ一つのメソッドは単一のトランザクションに関連付けが可能です。ネストもしくは複数トランザクションメソッド内で許可されません。

コンテナマネージドトランザクションはすべてのメソッドトランザクションに関連付けされることを要求しません。ビーン開発時には、ビーンメソッドトランザクションの関連付けを指定するトランザクション属性を設定可能です。

コンテナマネージドトランザクション境界を使用するエンタープライズビーンは、コンテナのトランザクション境界に干渉するトランザクション管理メソッドを使用してはいけません。そうしたメソッドの例としては、java.sql.Connectioncommit, setAutoCommit, rollbackメソッドや、javax.jms.Sessioncommitrollbackなどです。もしトランザクション境界を超える制御をしたい場合、アプリケーションマネージドトランザクション境界を使用する必要があります。

また、コンテナマネージドトランザクション境界を使用するエンタープライズビーンはjavax.transaction.UserTransactionインタフェースを使用してはいけません。

51.3.1 Transaction Attributes

トランザクション属性(transaction attribute)トランザクションのスコープを制御します。図 51-1は、なぜスコープ制御が重要なのかを図示しています。図では、method-Aトランザクションを開始し、Bean-2method-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

もしクライアントがトランザクション内で動作していてエンタープライズビーンのメソッドを呼び出す場合、コンテナは以下のステップを取ります。

  1. クライアントトランザクションサスペンドする。
  2. 新規トランザクションを開始する。
  3. メソッド呼び出しをデリゲートる。
  4. メソッド完了後にクライアントトランザクションを復元する。

クライアントがトランザクションに関連付けされていない場合、コンテナはメソッド実行前に新規のトランザクションを開始します。

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はトランザクション属性の影響をまとめたものです。T1T2トランザクションはコンテナ制御下です。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クラスのトランザクション属性にはNotSupportedfirstMethodにはRequiresNewsecondMethodにはRequiredが設定されています。メソッドに付与された@TransactionAttributeはクラスの@TransactionAttributeをオーバーライドするので、firstMethodの呼び出しは新規トランザクションを生成し、secondMethodの呼び出しは現在のトランザクション下で実行するか新規のトランザクションを開始するかのどちらかになります。thirdMethodfourthMethodトランザクション下には入りません。

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

コンテナが設定するトランザクション境界に干渉するメソッドは使用すべきではありません。以下のメソッドが禁止されています。

だたし、アプリケーションマネージドトランザクション境界でならこれらのメソッドは使用可能です。

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

JTAJava 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インターフェースのgetRollbackOnlysetRollbackOnlyメソッドは呼び出してはいけません。これらのメソッドはコンテナマネージドトランザクションでのみ使用すべきです。ビーンマネージドトランザクションでは、UserTransactionインターフェースのgetStatusrollbackを呼び出します。

51.5 Transaction Timeouts

コンテナマネージドトランザクションでは、Administration Consoleでトランザクションタイムアウト感覚を設定可能です。Starting the Administration Consoleを参照してください。

ビーンマネージドJTAトランザクションを使用するエンタープライズビーンでは、UserTransactionインターフェースのsetTransactionTimeoutメソッドを呼び出します。

51.5.1 To Set a Transaction Timeout

  1. Administration Consoleで、Configurationsの次にserver-configを開き、Transaction Serviceを選択する。
  2. Transaction Serviceページで、Transaction Timeoutフィールド値に任意の値(例えば5)を設定する。
    この設定では、トランザクションが5秒以内に完了しないとEJBコンテナがそのトランザクションロールバックします。
    デフォルト値は0で、トランザクションタイムアウトしないことを意味します。
  3. 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 を参照してください。

関連リンク