38 セッションレス・トランザクション

セッションレス・トランザクションは、トランザクションのライフサイクル中にトランザクションを柔軟に中断および再開できるトランザクションです。

セッションレス・トランザクションを使用する場合は、Oracle Database (シングル・インスタンスまたはマルチ・インスタンス)との通信時にトランザクション・マネージャを使用する必要はありません。そのトランザクションを調整する作業すべてが、データベースによって実行されます。さらに、アプリケーション側のロジックを必要とせずに、データベースによって内部的に2フェーズ・コミット(2PC)プロトコルが調整されます。

ノート:

この機能は、Oracle Databaseリリース23aiバージョン23.6以降でのみサポートされています。

38.1 セッションレス・トランザクションについて

セッションレス・トランザクションでは、トランザクションとセッションの間の結合が切断されています。トランザクションを開始した後に、それをセッションや接続に関連付ける必要はありません。

セッションまたは接続を解放して、それを別のクライアントで使用できるようにすることができます。また、XAプロトコルの調整にトランザクション・マネージャは必要ありません。これは、2フェーズ・コミット・プロセスとエラー・リカバリ・プロセスがデータベースによって自動的に処理されるためです。したがって、インダウト・トランザクションのリスクはなく、リカバリ・メカニズムの必要性はありません。セッションレス・トランザクション、そのユースケースおよび利点の詳細は、Database開発ガイドセッションレス・トランザクションを使用したアプリケーションの開発を参照してください。

ノート:

セッションレス・トランザクションは、単一データベースにのみ適用されます。セッションレス・トランザクションを使用して、複数のリソース・マネージャにまたがるトランザクションを管理しないでください。

38.2 JDBC APIの使用によるセッションレス・トランザクションの管理

この項で説明するJDBC APIを使用して、新しいトランザクションの開始、既存トランザクションの再開、トランザクションの中断、変更内容のコミットまたはロールバックによるトランザクションの終了を実行します。

これらのAPIは、oracle.jdbcパッケージのOracleConnectionインタフェースで使用できます。Oracle® Database JDBC Java APIリファレンスを参照してください。

この項の内容は次のとおりです:

38.2.1 セッションレス・トランザクションの開始

oracle.jdbcパッケージのOracleConnectionインタフェースにあるstartTransaction()メソッドを使用してセッションレス・トランザクションを開始します。トランザクションについて、一意のグローバル・トランザクションID (GTRID)およびタイムアウトを指定できます。

startTransaction()ではデータベースに対するラウンドトリップは実行されないため、これは非同期コールです。かわりに、Oracle JDBC Thinドライバにより、Oracle Databaseへの次のラウンドトリップの実行時に、セッションレス・トランザクションを開始するリクエストが送信されます。トランザクションは、サーバーからそのコールが正常に返された場合のみ開始されます。

セッションレス・トランザクションを開始するには、次のタスクを実行します:

  1. (オプション)セッションレス・トランザクションごとに、グローバル・トランザクションID (GTRID)と呼ばれる一意の識別子を指定します。一意の値を、1から64バイトまでのサイズのバイト配列として指定します。
    値を指定しなかった場合は、Oracle JDBC Thinドライバにより、GTRIDとして一意の値が生成されます。そのGTRIDを使用してトランザクションを開始から終了まで管理します。
  2. (オプション) timeoutを決定します。これにより、セッションレス・トランザクションsuspended状態のままにできる最長期間を指定します。中断されたセッションレス・トランザクションが指定期間内に再開されないと、Oracle Databaseによってそのトランザクションが取り消されます。

    タイムアウトを指定する際には、次の点を考慮してください。

    • 非常に大きい値を指定した場合は、トランザクションで、指定された期間、データベース・リソース(ロックされた行など)が保持される可能性があります。
    • 非常に小さい低い値(0など)を指定した場合は、セッションレス・トランザクションが中断され解放されるとすぐに、そのトランザクションが取り消されます。
    • デフォルト値は60秒です。
  3. 次のいずれかの方法で、OracleConnectionインタフェースのstartTransaction()メソッドをコールします。
    • このAPIでは、デフォルトのタイムアウトである60秒を使用して、セッションレス・トランザクションが開始されます。ドライバにより、そのトランザクション用に一意のGTRIDが作成されます。

      public byte[] startTransaction() throws SQLException;
    • このAPIでは、指定したタイムアウトで、セッションレス・トランザクションが開始されます。ドライバにより、そのトランザクション用に一意のGTRIDが作成されます。

      public byte[] startTransaction(int timeout) throws SQLException;
    • このAPIでは、指定したGTRID、およびデフォルトのタイムアウトである60秒を使用して、セッションレス・トランザクションが開始されます。

      public void startTransaction(byte[] GTRID) throws SQLException;
    • このAPIでは、指定したGTRIDおよびタイムアウトを使用して、セッションレス・トランザクションが開始されます。

      public void startTransaction(byte[] GTRID, int timeout) throws SQLException;

新しいセッションレス・トランザクションが開始されると、それがアクティブ状態になります。トランザクションのGTRIDは、一意である必要があります。新しいセッションレス・トランザクションを開始するリクエストを発行したときに、アクティブなセッションレス・トランザクションがすでに存在する場合は、Oracle Databaseによって既存のトランザクションが中断され、リクエストに基づいて新しいセッションレス・トランザクションが開始されます。

38.2.2 GTRIDの取得

現在アクティブ状態であるセッションレス・トランザクションのGTRIDを取得します。

セッションレス・トランザクションは、グローバル・トランザクションID (GTRID)と呼ばれる一意の識別子によって特定されます。GTRIDは、トランザクションを開始から終了まで管理するために使用されます。
  • 次のAPIにより、現在アクティブ状態であるセッションレス・トランザクションのGTRIDを取得します。
    byte[] getTransactionId() throwsSQLException;

    アクティブ状態のセッションレス・トランザクションがない場合は、nullが返されます。

GTRIDの取得では、そのセッションレス・トランザクションの、ローカルでの状態が反映されます。サーバーでの状態ではありません。次のシナリオを使用してこれについて説明します。startTransaction()は非同期コールであり、それによってデータベースに対するラウンドトリップは実行されません。かわりに、Oracle JDBC Thinドライバにより、Oracle Databaseへの次のラウンドトリップの実行時に、セッションレス・トランザクションを開始するリクエストが送信されます。トランザクションは、サーバーからそのコールが正常に返された場合のみ開始されます。

...
conn.startTransaction();
byte[] gtrid = getTransactionId();
...

トランザクションは次のラウンドトリップまではサーバー上で実際に開始されていませんが、getTransactionId()では、Oracle JDBC Thinドライバで生成されたGTRIDの値が返されます。

38.2.3 セッションレス・トランザクションの中断

アクティブなセッションレス・トランザクションを中断します。

  1. 次のいずれかの方法で、トランザクションを中断するメソッドをコールします。
    • 次のAPIを使用して、アクティブなセッションレス・トランザクションを同期的に中断します。これにより、Oracle Databaseに対するラウンドトリップが実行され、ただちにセッションレス・トランザクションが中断されます。

      void suspendTransactionImmediately() throws SQLException;
    • 次のAPIを使用して、アクティブなセッションレス・トランザクションを非同期的に中断します。suspendTransaction()ではデータベースに対するラウンドトリップは実行されないため、これは非同期コールです。かわりに、Oracle JDBC Thinドライバにより、Oracle Databaseに対する次のラウンドトリップの実行時に、セッションレス・トランザクションを中断するリクエストが送信されます。トランザクションは、サーバーからそのコールが正常に返された場合のみ中断されます。

      void suspendTransaction() throws SQLException;
    • トランザクションの中断前にデータベースに対して実行された最後の操作について把握している場合は、次のAPIを使用して、アクティブなセッションレス・トランザクションを中断します。これは、サーバーに対するラウンドトリップの回数を減らしてパフォーマンスを高めることができるため役に立ちます。ただし、executeUpdate()操作またはsuspend()操作のどちらかで例外がスローされると両方の操作が失敗することに注意してください。

      void executeUpdateAndSuspend() throwsSQLException;
アクティブ状態のセッションレス・トランザクションがない場合は、中断するアクティブなトランザクションがないため、操作は実行されません。このリクエストは無視され、エラー・メッセージは返されません。

38.2.4 セッションレス・トランザクションの再開

中断したセッションレス・トランザクションを再開します。

セッションレス・トランザクションが、それを再開するリクエストを送信するときに中断状態であった場合は、これによってそのトランザクションが再開されます。セッションレス・トランザクションは、トランザクションの再開をリクエストしたインスタンス上で再開されます。セッションレス・トランザクションが再開されると、それがアクティブ状態になります。

セッションにおいてトランザクションを再開するリクエストを送信したときに、そのセッションで別のセッションレス・トランザクションがすでにアクティブになっている場合は、Oracle Databaseによって、まずそのトランザクションの中断が試みられた後に、再開操作が実行されます。

resumeTransaction()ではデータベースに対するラウンドトリップは実行されないため、これは非同期コールです。かわりに、Oracle JDBC Thinドライバにより、Oracle Databaseへの次のラウンドトリップの実行時に、セッションレス・トランザクションを再開するリクエストが送信されます。トランザクションは、サーバーからそのコールが正常に返された場合のみ再開されます。

中断したセッションレス・トランザクションを再開するには:
  1. 再開するセッションレス・トランザクションのGTRIDを特定します。
  2. (オプション) timeoutを決定します。これにより、セッションレス・トランザクションsuspended状態のままにできる最長期間を指定します。中断されたセッションレス・トランザクションが指定期間内に再開されないと、Oracle Databaseによってそのトランザクションが取り消されます。

    タイムアウトを指定する際には、次の点を考慮してください。

    • 非常に大きい値を指定した場合は、トランザクションで、指定された期間、データベース・リソース(ロックされた行など)が保持される可能性があります。
    • 非常に小さい低い値(0など)を指定した場合は、セッションレス・トランザクションが中断され解放されるとすぐに、そのトランザクションが取り消されます。
    • デフォルト値は60秒です。
  3. OracleConnectionインタフェースにあるresumeTransaction()メソッドを、次のいずれかの方法でコールします。再開するトランザクションのGTRIDを指定する必要があります。
    • 次のAPIを使用して、デフォルトのタイムアウトである60秒の経過中にセッションレス・トランザクションを再開します。

      void resumeTransaction(byte[] GTRID) throws SQLException;
    • 次のAPIを使用して、指定したタイムアウトの経過中にセッションレス・トランザクションを再開します。

      void resumeTransaction(byte[] GTRID, int timeout) throws SQLException;

38.2.5

この項では、セッションレス・トランザクション機能を使用するように既存のアプリケーション・コードを変更する例を示します。

セッションレス・トランザクションを使用しない既存のサンプル・コード

アプリケーションで、開始するトランザクションの処理中にセッションのロックを回避する必要があり、いくつかの更新を加えてから、他のサービスに複数回コールを発行する場合について考えてみましょう。他のサービスから結果を取得した後は、トランザクションの処理を続行します。セッションレス・トランザクションを使用する場合に必要となる変更内容の比較のための参考として、コード例を次に示します。

次のコードでは、セッションレス・トランザクションを使用しないアプリケーションの実装例を示します。

PoolDataSource ds;

Connection conn = ds.getConnection();
conn.setAutoCommit(false);
// update database

// Fetch some data from other services(takes a while)

// do more updates on the database

conn.commit();
conn.close();

セッションレス・トランザクションを使用するための最適化されたサンプル・コード

次のサンプル・コードでは、セッションレス・トランザクションを使用するための最適化されたサンプル・コードを提供します。oracle.jdbcパッケージのOracleConnectionインタフェースにあるJDBC APIを使用してセッションレス・トランザクションを管理するには、次のサンプル・コードで示すように、接続オブジェクトをOracleConnectionにキャストする必要があります。

import oracle.jdbc.*;

PoolDataSource ds;
// GTRID is the unique identifier for a session.
byte[] gtrid;

// Acquire a session on instance 1.
Connection conn = ds.getConnection();
conn.setAutoCommit(false);
// Start a sessionless transaction with a unique identifier on instance 1
gtrid = (OracleConnection(conn)).startTransaction();
// Update database
(OracleConnection(conn)).conn.suspend();
conn.close();

// Fetch some data from other services, which takes a while

Connection conn2 = ds.getConnection();
conn2.setAutoCommit(false);
// Resume the sessionless transaction using the unique identifier,
// that you had defined earlier, on instance 2.
(OracleConnection(conn2)).resumeTransaction(gtrid);
// do more updates on the database
conn2.commit();
conn2.close();

38.3 既存のXA APIによるセッションレス・トランザクションの管理

この項では、既存のXAResourceインタフェースを使用してセッションレス・トランザクションを管理する方法について説明します。

セッションレス・トランザクションは、XAトランザクション用に使用されるAPIと互換性があります。Oracle JDBC ThinドライバでXA APIを使用してセッションレス・トランザクションを処理する必要がある場合は、oracle.jdbc.XAThroughSessionlessTransactionsシステム・プロパティをtrueに設定します。ドライバにより、トランザクション・マネージャによって提供されたXIDが、セッションレス・トランザクションの一意の識別子であるGTRIDに変換されます。デフォルトでは、oracle.jdbc.XAThroughSessionlessTransactionsfalseに設定されます。

制限事項:

  • セッションレス・トランザクションを使用できるのは、アプリケーションで、1つのサーバーまたはRACで実行されている単一のOracle Databaseに接続する場合のみです。
  • セッションレス・トランザクションに、他のリソース・マネージャ(別のデータベースなど)を関連付けることはできません。

XA APIを使用してセッションレス・トランザクションを管理する場合は、アプリケーションを実行する前に、コマンドラインを使用して次のシステム・プロパティ値を設定します。

  • コマンドラインで、javaコマンドの-Dオプションを使用してoracle.jdbc.XAThroughSessionlessTransactionsシステム・プロパティをtrueに設定します。
    java -Doracle.jdbc.XAThroughSessionlessTransactions=true

既存のアプリケーション・コードでXAトランザクションが使用されておりXAResourceインタフェースが実装されている場合は、同じコードを使用してセッションレス・トランザクションを管理できます。oracle.jdbc.XAThroughSessionlessTransactionsプロパティ値を設定すると、次の表で説明するように、XAResourceインタフェースにあるメソッドの動作が変更され、トランザクションがセッションレス・トランザクションとして処理されます。このようにして、既存のアプリケーションのコードを大幅に変更する必要なく、セッションレス・トランザクションのパフォーマンス改善によるメリットを得ることができます。

メソッド 説明
xaResource.start(xid, XAResource.TMNOFLAGS); セッションレス・トランザクションを開始します。
xaResource.start(xid, XAResource.TMRESUME); セッションレス・トランザクションを再開します。
(OracleXAResource).suspend(xid); セッションレス・トランザクションを中断します。
xaResource.end(xid1, XAResource.TMSUSPEND); セッションレス・トランザクションを中断します。
xaResource.end(xid2, XAResource.TMSUCCESS); セッションレス・トランザクションを中断します。
xaResource.setTransactionTimeout(seconds); トランザクション・タイムアウト値(秒)を設定します。
xaResource.commit(xid, true); セッションレス・トランザクションをコミットします。これは、まずトランザクションを再開してから、トランザクションをコミットします。
xaResource.rollback(xid); セッションレス・トランザクションをロールバックします。これは、まずトランザクションを再開してから、トランザクションをロールバックします。
xaResource.join(xid); セッションレス・トランザクションの場合、操作は実行されません。
xaResource.prepare(xid); セッションレス・トランザクションの場合、操作は実行されません。
xaResource.recover(XAResource.TMSTARTRSCAN); XAリカバリは、セッションレス・トランザクションではサポートされていません。これにより、例外がスローされます。
xaResource.forget(xid); セッションレス・トランザクションの場合、操作は実行されません。
xaResource1.isSameRM(xaResource2); セッションレス・トランザクションの場合、操作は実行されません。

XAResourceインタフェースの詳細は、https://docs.oracle.com/javase/8/docs/api/javax/transaction/xa/XAResource.htmlを参照してください。

OracleConnectionインタフェースで定義されているJDBC APIを使用して、クライアント・アプリケーションでセッションレス・トランザクションを管理することもできます。「JDBC APIの使用によるセッションレス・トランザクションの管理」を参照してください。