11.7. 実際のトランザクションの使用
11.7.1. トランザクション使用の概要
次の手順は、アプリケーションでトランザクションを使用する必要がある場合に役に立ちます。
11.7.2. トランザクションの制御
はじめに
この手順のリストでは、JTS API を使用するアプリケーションでトランザクションを制御するさまざまな方法を概説します。
11.7.2.1. トランザクションの開始
この手順では、新しいトランザクションの開始方法を示します。実行するトランザクションマネージャー (TM) が JTA または JTS のいずれかで設定されていれば API は同じになります。
UserTransaction
のインスタンスを取得します。@TransactionManagement(TransactionManagementType.BEAN)
アノテーションを用いると、JNDI、インジェクション、または EJB のコンテキスト EJB が Bean 管理のトランザクションを使用する場合) を使用してインスタンスを取得できます 。JNDI を使用してインスタンスを取得します。
new InitialContext().lookup("java:comp/UserTransaction")
インジェクションを使用してインスタンスを取得します。
@Resource UserTransaction userTransaction;
EJB コンテキストを使用してインスタンスを取得します。
ステートレス/ステートフル Bean の場合
@Resource SessionContext ctx; ctx.getUserTransaction();
メッセージ駆動型 Bean の場合
@Resource MessageDrivenContext ctx; ctx.getUserTransaction()
データソースに接続したら
UserTransaction.begin()
を呼び出します。try { System.out.println("\nCreating connection to database: "+url); stmt = conn.createStatement(); // non-tx statement try { System.out.println("Starting top-level transaction."); userTransaction.begin(); stmtx = conn.createStatement(); // will be a tx-statement ... } }
結果
トランザクションが開始します。トランザクションをコミットまたはロールバックするまで、データソースの使用はすべてトランザクションになります。
完全な例は、「JTA トランザクションの例」を参照してください。
EJB (CMT または BMT のいずれかと使用) の利点の 1 つは、コンテナーがトランザクション処理の内部をすべて管理することです。 そのため、ユーザーは JBoss EAP コンテナー間の XA トランザクションまたはトランザクションディストリビューションの一部であるトランザクションを処理する必要がありません。
11.7.2.1.1. ネストされたトランザクション
ネストされたトランザクションを用いると、アプリケーションは既存のトランザクションに埋め込まれるトランザクションを作成できます。このモデルでは、再帰的に複数のサブトランザクションをトランザクションに埋め込むことができます。親トランザクションをコミットまたはロールバックせずにサブトランザクションをコミットまたはロールバックできます。しかし、コミット操作の結果は、先祖のトランザクションがすべてコミットしたかどうかによって決まります。
実装固有の情報は、Narayana プロジェクトドキュメンテーションを参照してください。
ネストされたトランザクションは、JTS 仕様と使用した場合のみ利用できます。ネストされたトランザクションは JBoss EAP アプリケーションサーバーではサポートされない機能です。また、多くのデータベースベンダーがネストされたトランザクションをサポートしないため、ネストされたトランザクションをアプリケーションに追加する前にデータベースベンダーにお問い合わせください。
11.7.2.2. トランザクションのコミット
この手順では、Java Transaction API (JTA) を使用してトランザクションをコミットする方法を説明します。
要件
トランザクションは、コミットする前に開始する必要があります。トランザクションの開始方法は、「トランザクションの開始」を参照してください。
UserTransaction
でcommit()
メソッドを呼び出します。UserTransaction
で commit() メソッドを呼び出すと、TM はトラザクションのコミットを実行します。@Inject private UserTransaction userTransaction; public void updateTable(String key, String value) { EntityManager entityManager = entityManagerFactory.createEntityManager(); try { userTransaction.begin(); <!-- Perform some data manipulation using entityManager --> ... // Commit the transaction userTransaction.commit(); } catch (Exception ex) { <!-- Log message or notify Web page --> ... try { userTransaction.rollback(); } catch (SystemException se) { throw new RuntimeException(se); } throw new RuntimeException(ex); } finally { entityManager.close(); } }
CMT (Container Managed Transaction) を使用する場合は、手動でコミットする必要はありません。
Bean がコンテナー管理トランザクションを使用するよう設定すると、コンテナーはコードで設定したアノテーションに基づいてトランザクションライフサイクルを管理します。
@PersistenceContext private EntityManager em; @TransactionAttribute(TransactionAttributeType.REQUIRED) public void updateTable(String key, String value) <!-- Perform some data manipulation using entityManager --> ... }
結果
データソースがコミットされ、トランザクションが終了します。 そうでない場合は、例外が発生します。
完全な例は、「JTA トランザクションの例」を参照してください。
11.7.2.3. トランザクションのロールバック
この手順では、Java Transaction API (JTA) を使用してトランザクションをロールバックする方法を説明します。
要件
トランザクションは、ロールバックする前に開始する必要があります。トランザクションの開始方法は、「トランザクションの開始」を参照してください。
UserTransaction
でrollback()
メソッドを呼び出します。UserTransaction
のrollback()
メソッドを呼び出す場合、TM はトランザクションをロールバックし、データを前の状態に戻そうとします。@Inject private UserTransaction userTransaction; public void updateTable(String key, String value) EntityManager entityManager = entityManagerFactory.createEntityManager(); try { userTransaction.begin(): <!-- Perform some data manipulation using entityManager --> ... // Commit the transaction userTransaction.commit(); } catch (Exception ex) { <!-- Log message or notify Web page --> ... try { userTransaction.rollback(); } catch (SystemException se) { throw new RuntimeException(se); } throw new RuntimeException(e); } finally { entityManager.close(); } }
CMT (Container Managed Transaction) を使用する場合は、トランザクションを手動でロールバックする必要はありません。
Bean がコンテナー管理トランザクションを使用するよう設定すると、コンテナーはコードで設定したアノテーションに基づいてトランザクションライフサイクルを管理します。
CMT のロールバックは RuntimeException が発生すると実行されます。setRollbackOnly メソッドを明示的に呼び出してロールバックを発生させることもできます。または、 アプリケーション例外の @ApplicationException(rollback=true) を使用してロールバックできます。
結果
トランザクションは TM によりロールバックされます。
完全な例は、「JTA トランザクションの例」を参照してください。
11.7.3. トランザクションにおけるヒューリスティックな結果の処理方法
ヒューリスティックなトランザクションの結果はよく発生するものではなく、通常は例外的な原因が存在します。ヒューリスティックという言葉は「手動」を意味し、こうした結果は通常手動で処理する必要があります。トランザクションのヒューリスティックな結果は、「ヒューリスティックな結果」を参照してください。
この手順では、Java Transaction API (JTA) を使用してトランザクションのヒューリスティックな結果を処理する方法を説明します。
トランザクションのヒューリスティックな結果の原因は、リソースマネージャーがコミットまたはロールバックの実行を約束したにも関わらず、約束を守らなかったことにあります。原因としては、サードパーティーコンポーネント、サードパーティーコンポーネントと JBoss EAP 間の統合レイヤー、または JBoss EAP 自体の問題が考えられます。
ヒューリスティックなエラーの最も一般的な 2 つの原因は、環境での一時的な障害と、リソースマネージャー対応時のコーディングエラーです。
通常、環境内で一時的な障害が発生した場合は、ヒューリスティックなエラーを発見する前に気づくはずです。原因としては、ネットワークの停止、ハードウェア障害、データベース障害、電源異常などが考えられます。
ストレステストの実施中にテスト環境でヒューリスティックな結果が発生した場合は、テスト環境の脆弱性を意味します。
警告JBoss EAP は、障害発生時にヒューリスティックな状態ではないトランザクションを自動的にリカバリーしますが、ヒューリスティックなトランザクションのリカバリーは実行しません。
環境に明白な障害が発生していない場合や、ヒューリスティックな結果を簡単に再現できる場合は、おそらくコーディングエラーが原因です。サードパーティーベンダーに連絡して解決策があるかどうかを確認する必要があります。
JBoss EAP のトランザクションマネージャー自体に問題があることを疑う場合は、サポートチケットを作成する必要があります。
- 管理 CLI を使用して手動によるトランザクションのリカバリーを試すことができます。詳細は、JBoss EAP『Managing Transactions on JBoss EAP』の「Recovering a Transaction Participant」を参照してください。
トランザクションの結果を手作業で解決する処理は、障害の正確な状況によって異なります。環境に合わせて以下の手順を実行します。
- 関係しているリソースマネージャーを特定します。
- トランザクションマネージャーとリソースマネージャーの状態を調べます。
- 関係するコンポーネントの 1 つまたは複数で、ログの消去とデータの照合を手動で強制します。
テスト環境である場合や、データの整合性を気にしない場合は、トランザクションログを削除して JBoss EAP を再起動すると、ヒューリスティックな結果はなくなります。デフォルのトランザクションログの場所はスタンドアロンサーバーでは
EAP_HOME/standalone/data/tx-object-store/
ディレクトリー、管理対象ドメインではEAP_HOME/domain/servers/SERVER_NAME/data/tx-object-store/
ディレクトリーになります。管理対象ドメインの場合、 SERVER_NAME は、サーバーグループに参加している個々のサーバー名を示します。注記トラザクションログの場所は、使用中のオブジェクトストアや、
object-store-relative-to
およびobject-store-path
パラメーターに設定された値にも左右されます。標準のシャドーログや Apache ActiveMQ Artemis ログなどのファイルシステムログの場合、デフォルトディレクトリーの場所が使用されますが、JDBC オブジェクトストアを使用する場合は、トランザクションログはデータベースに保存されます。
11.7.4. JTA トランザクションのエラー処理
11.7.4.1. トランザクションエラーの処理
トランザクションエラーは、多くの場合、タイミングに依存するため、解決するのが困難です。以下に、一部の一般的なエラーと、これらのエラーのトラブルシューティングに関するヒントを示します。
これらのガイドラインはヒューリスティックエラーに適用されません。ヒューリスティックエラーが発生した場合は、トランザクションにおけるヒューリスティックな結果の処理方法 を参照し、Red Hat グローバルサポートサービスまでお問い合わせください。
- トランザクションがタイムアウトになったが、ビジネスロジックスレッドが認識しませんでした。
多くの場合、このようなエラーは、Hibernate がレイジーロードのデータベース接続を取得できない場合に発生します。頻繁に発生する場合は、タイムアウト値を増加できます。JBoss EAP『設定ガイド』の「トランザクションマネージャーの設定」を参照してください。
引き続き問題が解決されない場合、外部環境を調整して実行をさらに速くしたり、さらなる効率化のためにコードを再構築できることがあります。タイムアウトの問題が解消されない場合は、Red Hat グローバルサポートサービスにお問い合わせください。
- トランザクションがすでにスレッドで実行されているか、
NotSupportedException
例外が発生する NotSupportedException
例外は、通常、JTA トランザクションをネストしようとし、ネストがサポートされていないことを示します。トランザクションをネストしようとしないときは、多くの場合、スレッドプールタスクで別のトランザクションが開始されますが、トランザクションを中断または終了せずにタスクが終了します。通常、アプリケーションはこれを自動的に処理する
UserTransaction
を使用します。その場合は、フレームワークに問題があることがあります。コードで
TransactionManager
メソッドまたはTransaction
メソッドを直接使用する場合は、トランザクションをコミットまたはロールバックするときに次の動作に注意してください。コードでTransactionManager
メソッドを使用してトランザクションを制御する場合は、トランザクションをコミットまたはロールバックすると、現在のスレッドからトランザクションの関連付けが解除されます。ただし、コードでTransaction
メソッドを使用する場合は、トランザクションが実行されているスレッドと関連付けられていないことがあり、スレッドプールに返す前に手動でスレッドからトランザクションの関連付けを解除する必要があります。- 2 番目のローカルリソースを登録することはできません。
- このエラーは、2 番目の非 XA リソースをトランザクションに登録しようとした場合に、発生します。1 つのトランザクションで複数のリソースが必要な場合、それらのリソースは XA である必要があります。