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();
}