Oracle Database 12cリリース1 (12.1)では、トランザクション・ガード機能を導入し、計画済および計画外の停止と重複発行の際に最大1回の実行のための汎用インフラストラクチャを提供します。この章では、次の項で、Java用のトランザクション・ガードについて説明します。
現在のアプリケーションでは、保証されていて拡張性のある方法で最後のコミット操作の結果を判断する方法やサーバーとの通信エラーの後の処理は未解決の問題です。多くの場合、エンドユーザーは、重複したリクエストの再発行を回避するために特定の手順に従うように要求されます。たとえば、一部のアプリケーションでは、ユーザーに送信ボタンを2回クリックしないように警告します。これは、2回クリックした場合、ユーザーが意図せずに、同じものを2回購入し、2回分の支払いを行うことがあるためです。
この問題を解決するため、Java用のトランザクション・ガードではトランザクションが重複発行されても1件のみが有効になります。つまり、すべてのトランザクションの実行は最大1回であるため、アプリケーションは重複するトランザクションを送信できません。すべてのトランザクションは論理トランザクションID(LTXID
)でタグ付けされており、これは、トランザクションが障害の前にコミットしたかどうかを確認するために、障害の発生後に、アプリケーションによって使用されます。たとえば、コミット・コールが戻らない場合、アプリケーションはLTXID
を使用して、コールが正常終了したかどうかを検出できます。
関連項目: 『Oracle Database開発ガイド』 |
Java用のアプリケーション・コンティニュイティ機能はJava用のトランザクション・ガードを内部で使用しているため、透過的なセッション・リカバリと、処理中のトランザクションの初めからSQL文(問合せおよびDML)のリプレイが可能になります。アプリケーション・コンティニュイティによって計画済または計画外の停止発生後に作業のリカバリが可能になり、Java用のトランザクション・ガードによってトランザクションは重複発行されても必ず1件のみが有効になります。停止が発生すると、リカバリでは、障害が発生する前と同じ状態がリストアされます。
この項では、次のアクティビティのためにJava用のトランザクション・ガードに関連付けられたAPIについて説明します。
サーバーによって送信される現在の論理トランザクションIDを取得するには、oracle.jdbc.OracleConnection
インタフェースのgetLogicalTransactionId
メソッドを使用します。このメソッドをコールしても、データベースのラウンドトリップは発生しません。
例
OracleConnection oconn = (OracleConnection) ods.getConnection(); ... // Getting the 1st LTXID after connecting LogicalTransactionId firstLtxid = oconn.getLogicalTransactionId();
論理トランザクションIDに対する更新を受信するには、oracle.jdbc.LogicalTransactionIdEventListener
インタフェースを使用します。論理トランザクションIDイベントを処理するために、このインタフェースをアプリケーションに実装する必要があります。
リスナーを論理トランザクションIDイベントに登録するには、addLogicalTransactionIdEventListener
メソッドを使用します。
例
OracleConnection oconn = (OracleConnection) ods.getConnection(); ... // The subsequent LTXID updates can be obtained through the listener oconn.addLogicalTransactionIdEventListener(this);
addLogicalTransactionIdEventListener(LogicalTransactionIdEventListener listener, java.util.concurrent.Executor executor)
メソッドを使用して、リスナーをエグゼキュータに登録することもできます。
次に、トランザクション・ガードAPIを使用する完全な例を示します。
import oracle.jdbc.pool.OracleDataSource; import oracle.jdbc.OracleConnection; import oracle.jdbc.LogicalTransactionId; import oracle.jdbc.LogicalTransactionIdEvent; import oracle.jdbc.LogicalTransactionIdEventListener; public class transactionGuardExample { ... ... OracleDataSource ods = new OracleDataSource(); ods.setURL(url); ods.setUser("user"); ods.setPassword("password"); OracleConnection oconn = (OracleConnection) ods.getConnection(); // Getting the 1st LTXID after connecting LogicalTransactionId firstLtxid = oconn.getLogicalTransactionId(); // The subsequent LTXID updates can be obtained via the listener oconn.addLogicalTransactionIdEventListener(this); } public class LtxidListenerImpl implements LogicalTransactionIdEventListener { ... public void onLogicalTransactionIdEvent(LogicalTransactionIdEvent ltxidEvent) { LogicalTransactionId newLtxid = ltxidEvent.getLogicalTransactionId(); // process newLtxid ...... } }
DBMS_APP_CONT
パッケージには、サーバー側トランザクション・ガードAPIが含まれているGET_LTXID_OUTCOME
プロシージャが含まれています。このプロシージャを実行すると、トランザクションの結果が出力されます。トランザクションがコミットされていない場合、偽のトランザクションがコミットされます。そうでない場合、トランザクションの状態が戻されます。デフォルトでは、このパッケージのEXECUTE
権限がデータベース管理者に付与されています。
構文
PROCEDURE GET_LTXID_OUTCOME(CLIENT_LTXID IN RAW, committed OUT BOOLEAN, USER_CALL_COMPLETED OUT BOOLEAN);
入力パラメータ
CLIENT_LTXID
は、クライアント・ドライバのLTXID
を指定します。
出力パラメータ
COMMITTED
は、トランザクションがコミットされることを指定します。
USER_CALL_COMPLETED
は、トランザクションをコミットしたユーザー・コールが完了したことを指定します。
例外
サーバーがクライアントよりも先行している場合、SERVER_AHEAD
がスローされます。したがって、トランザクションは古くなり、すでにコミットされている必要があります。
クライアントがサーバーよりも先行している場合、CLIENT_AHEAD
がスローされます。これは、サーバーがフラッシュバックされるか、LTXID
が破損している場合にのみ、発生することがあります。これらの状況のいずれかの場合、結果は判定できません。
処理中にエラーが発生した場合、ERROR
がスローされ、結果は判定できません。現在のプロシージャの実行中に発生したエラー・コードを指定します。
例
例26-1に、GET_LTXID_OUTCOME
プロシージャをコールし、LTXID
の結果を確認する方法を示します。
例26-1 LTXIDの結果の確認
... OracleConnection oconn = (OracleConnection) ods.getConnection(); LogicalTransactionId ltxid = oconn.getLogicalTransactionId(); boolean committed = false; boolean call_completed = false; try { CallableStatement cstmt = oconn.prepareCall(GET_LTXID_OUTCOME); cstmt.setObject(1, ltxid); cstmt.registerOutParameter(2, OracleTypes.BIT); cstmt.registerOutParameter(3, OracleTypes.BIT); cstmt.execute(); committed = cstmt.getBoolean(2); call_completed = cstmt.getBoolean(3); System.out.println("LTXID committed ? " + committed); System.out.println("User call completed ? " + call_completed); } catch (SQLException sqlexc) { System.out.println("Calling GET_LTXID_OUTCOME failed"); sqlexc.printStackTrace(); }