ヘッダーをスキップ
Oracle Database JDBC開発者ガイドおよびリファレンス
10g リリース2(10.2)
B19275-03
  目次へ
目次
索引へ
索引

前へ
前へ
次へ
次へ
 

29 分散トランザクション

この章では、分散トランザクションのOracle Java Database Connectivity(JDBC)実装について説明します。分散トランザクションとは、協調してコミットされる必要があるマルチ・フェーズ・トランザクションで、多くの場合、複数のデータベースを使用します。関連するXAについても説明します。XAとは、分散トランザクションの汎用標準であり、Javaに限定されません。

次の項目が含まれます。


注意:

この章では、JDBC 2.0 Optional Packageの機能を説明します。これは、以前、JDBC 2.0 Standard Extension Application Program Interface(API)と呼ばれていたもので、Sun社のjavaxパッケージを通じて使用できます。Optional Packageは、Java Development Kit(JDK)1.4の一部です。JDK 1.2.xおよび1.3.xでは、関連パッケージはclasses12.jarファイルに含まれています。

分散トランザクションの詳細および一般情報は、Sun社のJDBC 2.0 Optional PackageおよびJava Transaction API(JTA)の仕様を参照してください。

概要

分散トランザクションは、グローバル・トランザクションと呼ばれることがあります。これは、関連のある複数のトランザクションの集合で、協調して管理される必要があります。分散トランザクションを構成するトランザクションは、同じデータベースを対象にすることもありますが、一般的には、異なる場所の異なるデータベースを対象にします。 分散トランザクションの個々のトランザクションを、トランザクション・ブランチと呼びます。

たとえば、分散トランザクションには、ある銀行のある口座から、別の銀行の口座への送金があります。両方の処理が正常に完了する保証がなければ、トランザクションはコミットできません。

JDBC 2.0 Extension APIでは、分散トランザクション機能が接続プーリング機能の最上位に構築されます。この分散トランザクション機能は、分散トランザクションのオープンXA標準にも基づいて構築されます。XAはX/Open標準の一部で、Java固有ではありません。

データベース・リソースに接続するには、JDBCを使用します。ただし、複数のデータベースに対するすべての変更を1回のトランザクションで更新するには、JTAグローバル・トランザクションでJDBC接続を使用する必要があります。トランザクションにデータベースSQL更新を含める処理は、データベース・リソースの参加と呼ばれます。

この項には、次の項目が含まれます。

分散トランザクションのコンポーネントおよびシナリオ

この項の以降の部分を読むとき、次のポイントを覚えておくと役に立ちます。

  • 分散トランザクション・システムは、通常、標準JTA機能を実装するソフトウェア・コンポーネントなど、外部のトランザクション・マネージャを使用して、個々のトランザクションを協調します。

    多くのベンダーが、XA準拠のJTAモジュールを提供しています。たとえば、Oracleからは、Oracle9i Application ServerおよびOracle Application Server 10gでJTAが提供されています。

  • XA機能は、通常、クライアント・アプリケーションから孤立しています。クライアント・アプリケーションではなく、アプリケーション・サーバーなど、中間層環境に実装されます。

    多くの場合、アプリケーション・サーバーとトランザクション・マネージャはともに中間層に位置します。同時に、アプリケーション・コードの一部も中間層に位置します。

  • この項の説明は、主に、中間層の開発者を対象にしています。

  • 分散トランザクションの説明では、リソース・マネージャという用語が頻繁に使用されます。リソース・マネージャとは、単に、データまたはその他のリソースを管理するエンティティです。この章で使用された場合、データベースを指します。


    注意:

    JTA機能を使用するには、CLASSPATHjta.jarを含める必要があります。このファイルは、ORACLE_HOME/jlibに格納されています。このファイルは、OracleのJDBC製品に含まれています。Sun社のWebサイトから取得することもできますが、オラクル社が提供するバージョンを使用することをお薦めします。このバージョンは、Oracleドライバでのテストが済んでいます。

分散トランザクションの概念

XA機能を使用する場合、トランザクション・マネージャが、XAリソース・インスタンスを使用して各トランザクション・ブランチを準備し、協調させ、すべてのトランザクション・ブランチを適切にコミットまたはロールバックします。

XA機能には、次のキー・コンポーネントが含まれています。

  • XAデータソース

    接続プーリング・データソースおよびその他のデータソースの拡張要素で、概念および機能の面で似ています。

    分散トランザクションで使用されるリソース・マネージャごとに1つのXAデータソース・インスタンスがあります。通常、XAデータソース・インスタンスは、中間層ソフトウェアで作成します。

    XAデータソースは、XA接続を作成します。

  • XA接続

    プーリングされた接続の拡張要素で、概念および機能の面で似ています。XA接続は、物理的なデータベース接続をカプセル化します。個々の接続インスタンスは、これら物理接続の一時的なハンドルです。

    XA接続インスタンスは、1つのOracleセッションに対応します。ただし、1つのセッションは、プーリングされた接続インスタンスのように、複数の論理接続インスタンスで順に使用できます。

    通常、XA接続インスタンスは、XAデータソース・インスタンスから、中間層ソフトウェアで取得します。分散トランザクションが、同じデータベースの複数セッションにかかわる場合、単一XAデータソース・インスタンスから複数のXA接続インスタンスを取得できます。

    XA接続は、OracleXAResourceインスタンスおよびJDBC接続インスタンスを作成します。

  • XAリソース

    分散トランザクションのトランザクション・ブランチを協調させるために、トランザクション・マネージャで使用されます。

    通常、OracleXAResourceインスタンスは、各XA接続インスタンスから1つずつ、中間層ソフトウェアで取得します。OracleXAResourceインスタンスとXA接続インスタンスの間には1対1の相関関係があります。同様に、OracleXAResourceインスタンスとOracleセッションの間には、1対1の相関関係があります。

    一般的なシナリオでは、中間層コンポーネントがOracleXAResourceインスタンスをトランザクション・マネージャに渡します。トランザクション・マネージャは、これを使用して分散トランザクションを協調させます。

    OracleXAResourceインスタンスは1つのOracleセッションに対応するので、OracleXAResourceインスタンスに対応付けられたトランザクション・ブランチのうち、同時にアクティブになれるものは1つのみです。ただし、他のトランザクション・ブランチを保留しておくことはできます。

    OracleXAResourceインスタンスには、そのOracleXAResourceインスタンスに対応付けられたセッションで実行中のトランザクション・ブランチの操作を、開始、終了、準備、コミットまたはロールバックする機能があります。

    準備処理は、2フェーズ・コミット操作の最初の処理です。トランザクション・マネージャは、各OracleXAResourceインスタンスにprepareを発行します。トランザクション・マネージャは、各トランザクション・ブランチの操作が正常に準備されたことを確認すると、各OracleXAResourceインスタンスにCOMMITを発行し、すべての変更をコミットします。

  • トランザクションID

    トランザクション・ブランチを識別するために使用されます。各トランザクションIDには、トランザクション・ブランチIDコンポーネントと分散トランザクションIDコンポーネントが含まれています。これにより、ブランチが分散トランザクションに対応付けられます。ある分散トランザクションに対応付けられたOracleXAResourceインスタンスには、すべて、同じ分散トランザクションIDコンポーネントが含まれているトランザクションIDが設定されます。

  • OracleXAResource.ORATRANSLOOSE

    トランザクションIDがxidの疎結合トランザクションを起動します。

ローカル・トランザクションとグローバル・トランザクションの切替え

JDBC 3.0では、アプリケーションは、ローカル・トランザクションとグローバル・トランザクションの間で接続を共有できます。また、アプリケーションは、ローカル・トランザクションとグローバル・トランザクションの間で接続を切り替えることもできます。

接続は必ず、次のモードのいずれかで行われます。

  • NO_TXN

    この接続を使用すると、トランザクションはアクティブになりません。

  • LOCAL_TXN

    この接続を使用すると、自動コミットがオフに設定されているか、無効化されているローカル・トランザクションがアクティブになります。

  • GLOBAL_TXN

    この接続を使用すると、グローバル・トランザクションがアクティブになります。

各接続では、その接続で実行されている操作によって、これらのモードが自動的に切り替わります。接続は、インスタンス化されたときは常にNO_TXNモードです。


注意:

各モードはOracle Databaseに関連付けられたJDBCドライバによって内部的に保持されます。

表29-1では、接続モードの推移のルールについて説明します。

表29-1 接続モードの推移

現在のモード NO_TXNに切り替わる場合 LOCAL_TXNに切り替わる場合 GLOBAL_TXNに切り替わる場合

NO_TXN

N/A

自動コミット・モードがFALSEで、Oracle Data Manipulation Language(DML)文が実行された場合

この接続を提供したXAconnectionから取得されたXAResourcestartがコールされた場合

LOCAL_TXN

次のいずれかの場合

  • Oracle Data Definition Language(DDL)文が実行された場合

  • commitがコールされた場合

  • rollbackがパラメータなしでコールされた場合

N/A

なし

GLOBAL_TXN

この接続でオープンしているグローバル・トランザクション内で、この接続を提供したXAconnectionから取得されたXAResourceendがコールされた場合

なし

N/A


前述のルールのいずれにも該当しない場合、モードは変わりません。

操作に関するモードの制限

現在の接続モードによって、トランザクション内で有効な操作が制限されます。

  • LOCAL_TXNモードの場合、アプリケーションでは、XAResourcestartpreparecommitrollbackforgetまたはendをコールしないでください。これらを起動すると、XAExceptionが発生します。

  • GLOBAL_TXNモードの場合、アプリケーションでは、java.sql.Connectioncommitrollbackrollback(Savepoint)setAutoCommit(true)またはsetSavepointをコールしないでください。また、oracle.jdbc.OracleConnectionでは、OracleSetSavepointまたはoracleRollbackをコールしないでください。これらを起動すると、SQLExceptionが発生します。


    注意:

    このモードに関する制限のエラー・チェックは、トランザクションとセーブポイントAPIに関する標準のエラー・チェックに追加されます。

Oracle XAパッケージ

Oracleからは、XA標準に従い分散トランザクション機能を実装するクラスを含む、次の3つのパッケージが提供されます。

  • oracle.jdbc.xa

  • oracle.jdbc.xa.client

  • oracle.jdbc.xa.server

XAデータソース、XA接続およびXAリソースのクラスは、clientパッケージとserverパッケージの両方に含まれています。それぞれの抽象クラスは、最上位のパッケージに含まれています。OracleXidおよびOracleXAExceptionクラスは、最上位のoracle.jdbc.xaパッケージに含まれています。これらの機能は、コードが実行される場所に依存しません。

中間層のシナリオでは、OracleXidOracleXAExceptionおよびoracle.jdbc.xa.clientパッケージをインポートします。

ただし、XAコードをターゲットOracle Databaseで実行する場合は、oracle.jdbc.xa.clientパッケージのかわりに、oracle.jdbc.xa.serverパッケージをインポートします。

ターゲット・データベースの内部で実行するコードでリモート・データベースにもアクセスする必要がある場合は、どちらのパッケージもインポートしません。かわりに、clientパッケージから使用するクラス(リモート・データベースにアクセスするとき)またはserverパッケージから使用するクラス(ローカル・データベースにアクセスするとき)の名前を完全に修飾する必要があります。クラス名は、これらのパッケージで重複しています。

XAコンポーネント

この項では、XAコンポーネントを説明します。JDBC 2.0 Optional Packageで指定されている標準XAインタフェースおよびこのインタフェースを実装するOracleクラスについて説明します。次の項目があります。

XADatasourceインタフェースとOracle実装

javax.sql.XADataSourceインタフェースは、XA接続のファクトリであるXAデータソースの標準機能のアウトラインを構成します。オーバーロードされたgetXAConnectionメソッドは、XA接続インスタンスを戻します。オプションで、入力としてユーザー名とパスワードを取ることもあります。

public interface XADataSource
{
   XAConnection getXAConnection() throws SQLException;
   XAConnection getXAConnection(String user, String password)
      throws SQLException;
   ...
}

Oracle JDBCは、XADataSourceインタフェースをOracleXADataSourceクラスで実装します。どちらも、oracle.jdbc.xa.clientパッケージおよびoracle.jdbc.xa.serverパッケージに含まれています。

OracleXADataSourceクラスは、OracleConnectionPoolDataSourceクラス(OracleDataSourceクラスの拡張)の拡張も行います。そのため、すべての接続プロパティも継承されます。

OracleXADataSourceクラスのgetXAConnectionメソッドは、XA接続インスタンスのOracle実装を戻します。このインスタンスは、OracleXAConnectionインスタンスです。


注意:

前述の非プーリング・データソースの場合と同じネーミング規則を使用して、XAデータソースをJava Naming Directory and Interface(JNDI)に登録できます。

setFastConnectionFailoverEnabled(true)をコールすることにより、OracleXADataSourceオブジェクトで高速接続フェイルオーバーを使用可能にできます。高速接続フェイルオーバーは、OracleXADataSourceオブジェクトで使用可能になると、OracleDataSourceオブジェクトの場合と同じように機能します。


関連項目:

高速接続フェイルオーバーの詳細は、第27章「高速接続フェイルオーバー」を参照してください。

XAConnectionインタフェースとOracle実装

XA接続インスタンスは、プーリングされた接続インスタンスと同様に、データベースへの物理接続をカプセル化します。このデータベースは、そのXA接続インスタンスを作成したXAデータソース・インスタンスの接続プロパティで指定されたデータベースです。

各XA接続インスタンスには、対応するOracleXAResourceインスタンスを作成する機能もあります。このインスタンスは、分散トランザクションを協調させるために使用されます。

XA接続インスタンスは、標準javax.sql.XAConnectionインタフェースを実装するクラスのインスタンスです。

public interface XAConnection extends PooledConnection
{
   javax.jta.xa.XAResource getXAResource() throws SQLException;
}

すでに説明したとおり、XAConnectionインタフェースはjavax.sql.PooledConnectionインタフェースを拡張します。そのため、getConnectioncloseaddConnectionEventListenerおよびremoveConnectionEventListenerメソッドも継承されます。

Oracle JDBCは、XAConnectionインタフェースをOracleXAConnectionクラスで実装します。どちらも、oracle.jdbc.xa.clientパッケージおよびoracle.jdbc.xa.serverパッケージに含まれています。

OracleXAConnectionクラスは、OraclePooledConnectionクラスも拡張します。

OracleXAConnectionクラスのgetXAResourceメソッドは、OracleXAResourceインスタンスのOracle実装を戻します。このインスタンスは、OracleXAResourceインスタンスです。getConnectionメソッドは、OracleConnectionインスタンスを戻します。

XA接続インスタンスから戻されるJDBC接続インスタンスは、物理接続のカプセル化ではなく、物理接続への一時的なハンドルとして機能します。物理接続は、XA接続インスタンスでカプセル化されます。XAConnectionオブジェクトから取得された接続は、グローバル・トランザクションに加わるまでは通常の接続とまったく同じように動作します。グローバル・トランザクションに加わった時点で、自動コミット・ステータスがFALSEに設定されます。グローバル・トランザクションが終了すると、自動コミット・ステータスはグローバル・トランザクション前の値に戻ります。XAConnectionから取得された接続でのデフォルトの自動コミット・ステータスは、Oracle Database 10gより前のすべてのリリースでは、falseです。Oracle Database 10gでは、デフォルトのステータスはtrueです。

XA接続インスタンスのgetConnectionメソッドがコールされるたびに、デフォルト動作を行う新しい接続インスタンスが戻されます。また、同じXA接続インスタンスから戻されて、まだ存在している前の接続インスタンスをすべてクローズします。ただし、前の接続インスタンスは、新しい接続インスタンスをオープンする前に、明示的にクローズすることをお薦めします。

XA接続インスタンスのcloseメソッドをコールすると、データベースへの物理接続がクローズされます。これは、通常、中間層で実行します。

XAResourceインタフェースとOracle実装

トランザクション・マネージャはOracleXAResourceインスタンスを使用して、分散トランザクションを構成するすべてのトランザクション・ブランチを協調させます。

OracleXAResourceインスタンスは、次の主要な機能を提供します。通常、トランザクション・マネージャから起動されます。

  • 分散トランザクションと、このOracleXAResourceインスタンスを作成したXA接続インスタンスで動作するトランザクション・ブランチの、対応付けおよび対応付けの解除を行います。基本的には、分散トランザクションを物理接続またはXA接続インスタンスでカプセル化されたセッションに対応付けます。これは、トランザクションIDを使用して実行されます。

  • 分散トランザクションの2フェーズ・コミット機能を実行します。すべてのトランザクション・ブランチで正常に処理されることが保証されるまで、その変更がどれか1つのトランザクション・ブランチでコミットされることはありません。


    注意:

    • XA接続インスタンスとOracleXAResourceインスタンスの間には常に1対1の相関関係があるので、対応付けられたXA接続インスタンスがクローズされると、OracleXAResourceインスタンスは暗黙的にクローズされます。

    • あるOracleXAResourceインスタンスがトランザクションをオープンした場合、そのトランザクションは同じOracleXAResourceインスタンスによってクローズする必要があります。


OracleXAResourceインスタンスは、標準javax.transaction.xa.XAResourceインタフェースを実装するクラスのインスタンスです。Oracle JDBCは、XAResourceインタフェースをOracleXAResourceクラスで実装します。どちらも、oracle.jdbc.xa.clientパッケージおよびoracle.jdbc.xa.serverパッケージに含まれています。

Oracle JDBCドライバは、OracleXAConnectionクラスのgetXAResourceメソッドがコールされると、OracleXAResourceインスタンスを作成して戻します。OracleXAResourceインスタンスを、接続インスタンスおよびその接続によって実行されるトランザクション・ブランチに対応付けるのも、Oracle JDBCドライバの役割です。

このメソッドは、特定の接続およびその接続で実行されるトランザクション・ブランチにOracleXAResourceインスタンスを関連付けるときのメソッドです。

OracleXAResourceメソッドの機能と入力パラメータ

OracleXAResourceクラスには、トランザクション・ブランチを、対応付けられた分散トランザクションと協調させるためのメソッドがいくつかあります。この機能は、通常、2フェーズのCOMMIT操作で起動されます。

通常、トランザクション・マネージャが、アプリケーション・サーバーなどの中間層コンポーネントからOracleXAResourceインスタンスを受け取り、この機能を起動します。

これらのメソッドは、XidインスタンスのフォームのトランザクションIDを入力とします。トランザクションIDには、トランザクション・ブランチIDコンポーネントと分散トランザクションIDコンポーネントが含まれています。各トランザクション・ブランチには一意のトランザクションIDが設定されていますが、同じグローバル・トランザクションに属するトランザクション・ブランチは、トランザクションIDの一部として同じグローバル・トランザクション・コンポーネントを持っています。

Start

トランザクション・ブランチの処理を開始します。トランザクション・ブランチを分散トランザクションに対応付けます。

void start(Xid xid, int flags)

flagsパラメータには、次の値のうち1つ以上を設定する必要があります。

  • XAResource.TMNOFLAGS

    このXAリソース・インスタンスに対応付けられたセッションの後続の操作のために、新しいトランザクション・ブランチを起動することを示します。このブランチには、トランザクションID xidが設定されます。これは、トランザクション・マネージャによって作成されたOracleXidインスタンスです。これにより、トランザクション・ブランチは適切な分散トランザクションにマップされます。

  • XAResource.TMJOIN

    このXAリソース・インスタンスに対応付けられたセッションの後続の操作を、xidで指定した既存のトランザクション・ブランチに結合します。

  • XAResource.TMRESUME

    xidで指定したトランザクション・ブランチを再開します。


    注意:

    再開できるのは、前に保留されていたトランザクション・ブランチのみです。

  • OracleXAResource.ORATMSERIALIZABLE

    トランザクションIDがxidのシリアライズ可能なトランザクションを起動します。

  • OracleXAResource.ORATMREADONLY

    トランザクションIDがxidの読取り専用トランザクションを起動します。

  • OracleXAResource.ORATMREADWRITE

    トランザクションIDがxidの読取り/書込みトランザクションを起動します。

  • OracleXAResource.ORATRANSLOOSE

    トランザクションIDがxidの疎結合トランザクションを起動します。

TMNOFLAGSTMJOINTMRESUMEORATMSERIALIZABLEORATMREADONLYおよびORATMREADWRITE は、XAResourceインタフェースおよびOracleXAResourceクラスのstaticメンバーとして定義されています。ORATMSERIALIZABLEORATMREADONLYおよびORATMREADWRITEは、分離モード・フラグです。デフォルトの分離動作は、READ COMMITTEDです。


注意:

  • TMRESUMEを指定してstartメソッドを使用するかわりに、トランザクション・マネージャでOracleXAResourceにキャストし、Oracle拡張機能resume(Xid xid)メソッドを使用することもできます。

  • TMRESUMEを使用する場合は、start(xid, XAResource.TMRESUME | OracleXAResource.TMNOMIGRATE)のように、TMNOMIGRATE も使用する必要があります。これによって、アプリケーションでエラー「ORA-1002: フェッチ順序が無効です。」を受け取ることがなくなります。

  • 分離モード・フラグを正しく使用しないと、コードXAER_INVALの例外が発生します。また、グローバル・トランザクションの再開時に分離モードを使用することはできません。これは、既存のトランザクションに分離レベルを設定できないためです。トランザクションの再開時に分離モードを使用しようとすると、コードORA-24790の外部Oracle例外が発生します。

  • エラー「ORA-1002: フェッチ順序が無効です。」を回避するには、TMNOMIGRATEフラグをstartメソッドに含めます。たとえば、次のようになります。

    start(xid, XAResource.TMSUSPEND | OracleXAResource.TMNOMIGRATE);
    
  • OracleXAResourceで定義されているフラグはすべてOracle拡張機能です。これらのフラグを使用するトランザクション・マネージャの作成時には、この点を留意してください。


トランザクション・ブランチを起動するときに適切なトランザクションIDを作成するには、そのトランザクション・ブランチが属する分散トランザクションを、トランザクション・マネージャに指示する必要があります。このメカニズムは、中間層とトランザクション・マネージャの間で処理されます。

End

xidで指定されたトランザクション・ブランチの処理を終了します。トランザクション・ブランチと分散トランザクションの対応付けを解除します。

void end(Xid xid, int flags)

flagsパラメータには、次の値のうち1つを設定できます。

  • XAResource.TMSUCCESS

    このトランザクション・ブランチが正常であることを示します。

  • XAResource.TMFAIL

    このトランザクション・ブランチが異常であることを示します。

  • XAResource.TMSUSPEND

    xidで指定したトランザクション・ブランチを保留することを示します。トランザクション・ブランチを保留することにより、複数のトランザクション・ブランチを単一セッションで使用できます。ただし、ある時点でアクティブにできるのは、1つのみです。また、多くの場合、リソースの観点から見て、2つのセッションを使用するよりも高価です。

TMSUCCESSTMFAILおよびTMSUSPENDは、XAResourceインタフェースおよびOracleXAResourceクラスの静的メンバーとして定義されています。


注意:

  • TMSUSPENDを指定してendメソッドを使用するかわりに、トランザクション・マネージャでOracleXAResourceにキャストし、Oracle拡張機能suspend(Xid xid)メソッドを使用することもできます。

  • この、トランザクションを保留するXA機能によって、単一JDBC接続で、各種トランザクションを切り替えることができます。分散トランザクション環境になく、XAクラスを必要としない場合でも、この機能を実現するためにXAクラスを使用できます。

  • TMSUSPENDを使用する場合は、end(xid, XAResource.TMSUSPEND | OracleXAResource.TMNOMIGRATE)のように、TMNOMIGRATE も使用する必要があります。これによって、アプリケーションでエラー「ORA-1002: フェッチ順序が無効です。」を受け取ることがなくなります。

  • エラー「ORA 1002: フェッチ順序が無効です。」を回避するには、TMNOMIGRATEフラグをendメソッドに含めます。たとえば、次のようになります。

    end(xid, XAResource.TMSUSPEND | OracleXAResource.TMNOMIGRATE);
    
    
    
  • OracleXAResourceで定義されているフラグはすべてOracle拡張機能です。これらのフラグを使用するトランザクション・マネージャの場合は、この点を留意してください。


Prepare

xidで指定したトランザクション・ブランチで実行される変更の準備をします。これは、2フェーズ・コミット操作の最初のフェーズです。データベースがアクセス可能で、変更が正常にコミットされることを確認します。

int prepare(Xid xid)

このメソッドでは、次の整数値を戻します。

  • XAResource.XA_RDONLY

    トランザクション・ブランチが、SELECT文など、読取り専用操作のみを実行する場合に戻されます。

  • XAResource.XA_OK

    トランザクション・ブランチが更新を実行する場合、すべて準備済で、エラーがなければ、この値が戻されます。

  • N/A(戻り値なし)

    トランザクション・ブランチが更新を実行する場合、そのうち1つでもエラーが発生すると、値は戻されません。この場合、XA例外が発生します。

XA_RDONLYおよびXA_OKは、XAResourceインタフェースおよびOracleXAResourceクラスのstaticメンバーとして定義されています。


注意:

  • prepareメソッドをコールする前に、必ず、ブランチのendメソッドをコールする必要があります。

  • 分散トランザクションにあるトランザクション・ブランチが1つのみの場合、prepareメソッドをコールする必要はありません。準備なしに、OracleXAResourcecommitメソッドをコールできます。


Commit

xidで指定したトランザクション・ブランチで準備された変更をコミットします。これは、2フェーズ・コミットの2番目のフェーズです。すべてのトランザクション・ブランチが正常に準備された後でのみ実行します。

void commit(Xid xid, boolean onePhase)

onePhaseパラメータは、次のように設定します。

  • true

    トランザクション・ブランチをコミットするときに、2フェーズ・プロトコルではなく、1フェーズ・プロトコルを使用します。分散トランザクションにトランザクション・ブランチが1つのみある場合に適しています。prepare処理はスキップされます。

  • false

    トランザクション・ブランチをコミットするときに、2フェーズ・プロトコルを使用します。

Roll back

xidで指定したトランザクション・ブランチで準備された変更をロールバックします。

void rollback(Xid xid)

Forget

リソース・マネージャに、ヒューリスティックに完了したトランザクション・ブランクを破棄するよう通知します。

public void forget(Xid xid)

Recover

トランザクション・マネージャは、リカバリ中にこのメソッドをコールして、現在準備中またはヒューリスティックに完了した状態のトランザクション・ブランチのリストを取得します。

public Xid[] recover(int flag)

注意:

TMSTARTRSCANTMENDRSCANまたはTMNOFLAGS以外の値をflagに使用すると、例外が発生します。そうでない場合、flagは無視されます。

リソース・マネージャは、現在準備中またはヒューリスティックに完了した状態のトランザクション・ブランチに対して、0(ゼロ)以上のXidを戻します。操作中にエラーが発生した場合は、リソース・マネージャにより適切なXAExceptionが発行されます。

同じRMのチェック

2つのOracleXAResourceインスタンスが同じリソース・マネージャに対応しているかどうかを判断するには、一方のOracleXAResourceインスタンスから、入力としてもう一方のOracleXAResourceインスタンスを指定して、isSameRMメソッドをコールします。次の例では、xares1およびxares2OracleXAResourceインスタンスとします。

boolean sameRM = xares1.isSameRM(xares2);

XidインタフェースとOracle実装

トランザクション・マネージャは、トランザクションIDインスタンスを作成し、これを使用して、分散トランザクションのブランチを協調させます。各トランザクション・ブランチには、一意なトランザクションIDが割り当てられます。トランザクションIDには、次の情報が含まれます。

  • フォーマット識別子

    フォーマット識別子は、Javaトランザクション・マネージャを指定します。たとえば、フォーマット識別子ORCLがあります。このフィールドは、NULLにはできません。フォーマット識別子のサイズは4バイトです。

  • グローバル・トランザクション識別子

    分散トランザクションIDコンポーネントとも呼ばれます。グローバル・トランザクション識別子のサイズは64バイトです。

  • ブランチ修飾子

    トランザクション・ブランチIDコンポーネントとも呼ばれます。ブランチ修飾子のサイズは64バイトです。

64バイトのグローバル・トランザクション識別子の値は、同じ分散トランザクションに属するトランザクション・ブランチすべてのトランザクションIDで同一です。ただし、トランザクションIDの全体は、トランザクション・ブランチごとに一意です。

XAトランザクションIDインスタンスは、標準javax.transaction.xa.Xidインタフェースを実装するクラスのインスタンスです。このインタフェースは、X/Openトランザクション識別子XID構造体のJavaマッピングです。

Oracleでは、このインタフェースをoracle.jdbc.xaパッケージのOracleXidクラスに実装しています。OracleXidインスタンスは、トランザクション・マネージャでのみ使用されますが、アプリケーション・プログラムまたはアプリケーション・サーバーにとって透過的です。


注意:

Oracleでは、OracleXAResourceのコールにOracleXidを使用する必要はありません。かわりに、javax.transaction.xa.Xidインタフェースを実装するクラスを使用します。

トランザクション・マネージャでは、次のメソッドを使用して、OracleXidインスタンスを作成できます。

public OracleXid(int fId, byte gId[], byte bId[]) throws XAException

fIdは、フォーマット識別子を表す整数値です。gId[]は、グローバル・トランザクション識別子を表すバイト配列です。bId[]は、ブランチ修飾子を表すバイト配列です。

Xidインタフェースでは、次のgetterメソッドが指定されています。

  • public int getFormatId()

  • public byte[] getGlobalTransactionId()

  • public type[] getBranchQualifier()

エラー処理と最適化

この項では、XA例外の機能とエラー処理およびXA実装でのOracle最適化について説明します。次の項目が含まれます。

例外とエラー処理の説明には、標準XA例外クラスとOracle固有のXA例外クラスが含まれます。また、特定のXAエラー・コードおよびエラー処理の方法も含まれます。

XAExceptionクラスとメソッド

XAメソッドでは、一般例外やSQLExceptionsではなく、XA例外が発生します。XA例外は、標準クラスjavax.transaction.xa.XAExceptionまたはそのサブクラスのインスタンスです。Oracleでは、XAExceptionoracle.jdbc.xa.OracleXAExceptionクラスでサブクラス化されています。

OracleXAExceptionインスタンスは、Oracleエラー部とXAエラー部で構成されています。次のコンストラクタのいずれかを使用して構築されます。

public OracleXAException()

public OracleXAException(int error)

エラー値は、Oracle SQLエラー値とXAエラー値を組み合せたエラー・コードです。Oracleエラー値とXAエラー値を組み合せる正確な方法は、JDBCドライバによって判断されます。

OracleXAExceptionクラスには、次のメソッドが含まれます。

  • public int getOracleError()

    このメソッドは、例外に含まれるOracle SQLエラー・コード(標準ORAエラー番号)を戻します。Oracle SQLエラーがなければ、0を戻します。

  • public int getXAError()

    このメソッドは、例外に含まれるXAエラー・コードを戻します。XAエラー値は、javax.transaction.xa.XAExceptionクラスで定義されています。

OracleエラーとXAエラーのマッピング

表29-2で示すように、OracleエラーはOracleXAExceptionインスタンスのXAエラーに対応しています。

表29-2 OracleとXAのエラー・マッピング

Oracleエラー・コード XAエラー・コード

ORA 03113

XAException.XAER_RMFAIL

ORA 03114

XAException.XAER_RMFAIL

ORA 24756

XAException.XAER_NOTA

ORA 24764

XAException.XA_HEURCOM

ORA 24765

XAException.XA_HEURRB

ORA 24766

XAException.XA_HEURMIX

ORA 24767

XAException.XA_RDONLY

ORA 25351

XAException.XA_RETRY

その他のORAエラーすべて

XAException.XAER_RMERR


XAエラー処理

次の例では、OracleXAExceptionクラスを使用して、XA例外を処理します。

try {
   ...
   ...Perform XA operations...
   ...
} catch(OracleXAException oxae) {
  int oraerr = oxae.getOracleError();
  System.out.println("Error " + oraerr);
}
  catch(XAException xae)
{...Process generic XA exception...}

XA操作によってOracle固有のXA例外が発生しなかった場合、このコードは一般XA例外の処理を行いません。

Oracle XA最適化

Oracle JDBCには、分散トランザクションの2つ以上のブランチが同じデータベース・インスタンスを使用する場合、つまり、これらのブランチに対応付けられたOracleXAResourceインスタンスが同じリソース・マネージャに対応付けられている場合、パフォーマンスを改善する機能があります。

このような場合、これらのOracleXAResourceインスタンスのうち1つのprepareメソッドのみがXA_OKを戻すか、または失敗します。残りは、更新が行われる場合でも、XA_RDONLYを戻します。これにより、トランザクション・マネージャは、すべてのトランザクション・ブランチを暗黙的に結合し、XA_OKを戻した(または失敗した)OracleXAResourceインスタンスによって、結合されたトランザクションをコミット(失敗した場合はロールバック)できます。

トランザクション・マネージャは、OracleXAResourceクラスのisSameRMメソッドを使用して、2つのOracleXAResourceインスタンスが同じリソース・マネージャを使用しているかどうかを判断できます。このようにして、XA_RDONLY戻り値の意味を解析できます。

分散トランザクションの実装

この項では、Oracle XA機能を使用して分散トランザクションを実装する方法の例を示します。この項には、次の項目が含まれます。

Oracle XAのインポートのサマリー

Oracle XA機能を使用するには、次のパッケージをインポートする必要があります。

import oracle.jdbc.xa.OracleXid;
import oracle.jdbc.xa.OracleXAException;
import oracle.jdbc.pool.*;
import oracle.jdbc.xa.client.*;
import javax.transaction.xa.*;

oracle.jdbc.poolパッケージには、接続プーリング機能のクラスが含まれています。この一部は、XA関連クラスによってサブクラス化されています。

また、コードがOracle Databaseの内部で実行され、そのデータベースにアクセスしてSQL操作を行う場合は、oracle.jdbc.xa.clientではなく、oracle.jdbc.xa.serverをインポートする必要があります。

import oracle.jdbc.xa.server.*;

サーバー側Thinドライバを使用して、アプリケーションがXAトランザクションの一部として別のOracle Databaseにアクセスする必要がある場合は、コードで完全に修飾されたoracle.xa.clientクラスの名前を使用できます。

clientおよびserverパッケージには、それぞれのバージョンのOracleXADataSourceOracleXAConnectionおよびOracleXAResourceクラスがあります。これら3つのクラスの抽象バージョンは、最上位のoracle.jdbc.xaパッケージに含まれています。

OracleのXAコード・サンプル

このサンプルでは、異なるデータベースに対する2つのトランザクション・ブランチで、2フェーズ分散トランザクションを使用します。

簡単にするため、このサンプルでは、通常は中間層に置くコードと、通常はトランザクション・マネージャに置くコード(OracleXAResourceメソッドの起動や、トランザクションIDの作成など)を組み合せているので、注意してください。

短くするため、トランザクションID作成の指定およびSQL操作の実行は、ここでは示しません。完全な例は製品に付属しています。

このサンプルは、次の順序で実行します。

  1. トランザクション・ブランチ#1を開始します。

  2. トランザクション・ブランチ#2を開始します。

  3. ブランチ#1のDML操作を実行します。

  4. ブランチ#2のDML操作を実行します。

  5. トランザクション・ブランチ#1を終了します。

  6. トランザクション・ブランチ#2を終了します。

  7. ブランチ#1を準備します。

  8. ブランチ#2を準備します。

  9. ブランチ#1をコミットします。

  10. ブランチ#2をコミットします。

// You need to import the java.sql package to use JDBC
import java.sql.*;
import javax.sql.*;
import oracle.jdbc.*;
import oracle.jdbc.pool.*;
import oracle.jdbc.xa.OracleXid;
import oracle.jdbc.xa.OracleXAException;
import oracle.jdbc.xa.client.*;
import javax.transaction.xa.*;

class XA4
{
  public static void main (String args [])
       throws SQLException
  {

    try
    {
        String URL1 = "jdbc:oracle:oci:@";
        // You can put a database name after the @ sign in the connection URL.
        String URL2 ="jdbc:oracle:thin:@(description=(address=(host=dlsun991)
                     (protocol=tcp)(port=5521))(connect_data=(sid=rdbms2)))";
        // Create first DataSource and get connection
        OracleDataSource ods1 = new OracleDataSource();
        ods1.setURL(URL1);
        ods1.setUser("scott");
        ods1.setPassword("tiger");
        Connection conna = ods1.getConnection();

        // Create second DataSource and get connection
        OracleDataSource ods2 = new OracleDataSource();
        ods2.setURL(URL2);
        ods2.setUser("scott");
        ods2.setPassword("tiger");
        Connection connb = ods2.getConnection();

        // Prepare a statement to create the table
        Statement stmta = conna.createStatement ();

        // Prepare a statement to create the table
        Statement stmtb = connb.createStatement ();

        try
        {
          // Drop the test table
          stmta.execute ("drop table my_table");
        }
        catch (SQLException e)
        {
          // Ignore an error here
        }

        try
        {
          // Create a test table
          stmta.execute ("create table my_table (col1 int)");
        }
        catch (SQLException e)
        {
          // Ignore an error here too
        }

        try
        {
          // Drop the test table
          stmtb.execute ("drop table my_tab");
        }
        catch (SQLException e)
        {
          // Ignore an error here
        }

        try
        {
          // Create a test table
          stmtb.execute ("create table my_tab (col1 char(30))");
        }
        catch (SQLException e)
        {
          // Ignore an error here too
        }

        // Create XADataSource instances and set properties.
        OracleXADataSource oxds1 = new OracleXADataSource();
        oxds1.setURL("jdbc:oracle:oci:@");
        oxds1.setUser("scott");
        oxds1.setPassword("tiger");

        OracleXADataSource oxds2 = new OracleXADataSource();

        oxds2.setURL("jdbc:oracle:thin:@(description=(address=(host=dlsun991)
                   (protocol=tcp)(port=5521))(connect_data=(sid=rdbms2)))");
        oxds2.setUser("scott");
        oxds2.setPassword("tiger");

        // Get XA connections to the underlying data sources
        XAConnection pc1  = oxds1.getXAConnection();
        XAConnection pc2  = oxds2.getXAConnection();

        // Get the physical connections
        Connection conn1 = pc1.getConnection();
        Connection conn2 = pc2.getConnection();

        // Get the XA resources
        XAResource oxar1 = pc1.getXAResource();
        XAResource oxar2 = pc2.getXAResource();

        // Create the Xids With the Same Global Ids
        Xid xid1 = createXid(1);
        Xid xid2 = createXid(2);

        // Start the Resources
        oxar1.start (xid1, XAResource.TMNOFLAGS);
        oxar2.start (xid2, XAResource.TMNOFLAGS);

        // Execute SQL operations with conn1 and conn2
        doSomeWork1 (conn1);
        doSomeWork2 (conn2);

        // END both the branches -- IMPORTANT
        oxar1.end(xid1, XAResource.TMSUCCESS);
        oxar2.end(xid2, XAResource.TMSUCCESS);

        // Prepare the RMs
        int prp1 =  oxar1.prepare (xid1);
        int prp2 =  oxar2.prepare (xid2);

        System.out.println("Return value of prepare 1 is " + prp1);
        System.out.println("Return value of prepare 2 is " + prp2);

        boolean do_commit = true;

        if (!((prp1 == XAResource.XA_OK) || (prp1 == XAResource.XA_RDONLY)))
           do_commit = false;

        if (!((prp2 == XAResource.XA_OK) || (prp2 == XAResource.XA_RDONLY)))
           do_commit = false;

       System.out.println("do_commit is " + do_commit);
        System.out.println("Is oxar1 same as oxar2 ? " + oxar1.isSameRM(oxar2));

        if (prp1 == XAResource.XA_OK)
          if (do_commit)
             oxar1.commit (xid1, false);
          else
             oxar1.rollback (xid1);

        if (prp2 == XAResource.XA_OK)
          if (do_commit)
             oxar2.commit (xid2, false);
          else
             oxar2.rollback (xid2);

         // Close connections
        conn1.close();
        conn1 = null;
        conn2.close();
        conn2 = null;

        pc1.close();
        pc1 = null;
        pc2.close();
        pc2 = null;

        ResultSet rset = stmta.executeQuery ("select col1 from my_table");
        while (rset.next())
          System.out.println("Col1 is " + rset.getInt(1));

        rset.close();
        rset = null;

        rset = stmtb.executeQuery ("select col1 from my_tab");
        while (rset.next())
          System.out.println("Col1 is " + rset.getString(1));

        rset.close();
        rset = null;

        stmta.close();
        stmta = null;
        stmtb.close();
        stmtb = null;

        conna.close();
        conna = null;
        connb.close();
        connb = null;

    } catch (SQLException sqe)
    {
      sqe.printStackTrace();
    } catch (XAException xae)
    {
      if (xae instanceof OracleXAException) {
        System.out.println("XA Error is " +
                      ((OracleXAException)xae).getXAError());
        System.out.println("SQL Error is " +
                      ((OracleXAException)xae).getOracleError());
      }
    }
  }

  static Xid createXid(int bids)
    throws XAException
  {...Create transaction IDs...}

  private static void doSomeWork1 (Connection conn)
   throws SQLException
  {...Execute SQL operations...}

  private static void doSomeWork2 (Connection conn)
   throws SQLException
  {...Execute SQL operations...}
}

Oracle JDBCドライバのネイティブXA

通常、XAコマンドは次の方法でサーバーに送信できます。

この2つの方法でサーバーにXAコマンドを送信する場合、パフォーマンスに大きな相違があります。PL/SQLプロシージャと比較して、ネイティブAPIを使用するほうが、高いパフォーマンスを達成できます。

Oracle Database 10gより前のリリースでは、ThinドライバはPL/SQLプロシージャを使用してXAコマンドをサーバーに送信していました。これは、ThinネイティブAPIを使用できなかったためです。Oracle Database 10gでは、ThinネイティブAPIを使用できるようになったため、デフォルトではこのAPIを使用してXAコマンドを送信します。

この項には、次の項目が含まれます。

OCIネイティブXA

ネイティブXAは、OracleXADataSourceクラスのtnsEntryおよびnativeXAプロパティを使用して有効化します。


関連項目:

これらのプロパティの説明は、表9-2「Oracle拡張データソース・プロパティ」を参照してください。


注意:

現在、OCIネイティブXAは、マルチスレッド環境では使用できません。OCIドライバは、OracleのC/XAライブラリを使用して分散トランザクションをサポートしています。OCIドライバでは、グローバル・トランザクションの再開前に、各スレッドのXAConnectionを取得する必要があります。

構成およびインストール

Sun Solarisシステムの場合、ネイティブXA機能を使用可能にするには、共有ライブラリlibheteroxa10.soおよびlibheteroxa10_g.soが必要です。ネイティブXA機能を正しく動作させるには、これらのライブラリをインストールして、Sun Solarisの検索パスで使用可能にする必要があります。

Microsoft Windowsシステムの場合、ネイティブXA機能を使用可能にするには、heteroxa10.dllおよびheteroxa10_g.dllファイルが必要です。ネイティブXA機能を正しく動作させるには、これらのファイルをインストールして、WindowsのDLLパスで使用可能にする必要があります。


注意:

__gという接尾辞が付いたライブラリは、デバッグ・ライブラリです。

例外処理

分散トランザクションでネイティブXA機能を使用する場合は、アプリケーションでOracleXAExceptionまたはOracleSQLExceptionをチェックするのではなく、単純にXAExceptionまたはSQLExceptionをチェックすることをお薦めします。


関連項目:

ネイティブXAメッセージのリストについては、ネイティブXAメッセージを参照してください。


注意:

標準のXAエラー・コードに対するSQLエラー・コードのマッピングは、ネイティブXA機能には適用されません。

ネイティブXAのコード・サンプル

次のコードは、ネイティブXA機能を使用可能にする方法を示します。

...
// Create a XADataSource instance
OracleXADataSource oxds = new OracleXADataSource();
oxds.setURL(url);

// Set the nativeXA property to use Native XA feature
oxds.setNativeXA(true);

// Set the tnsEntry property to an older DB as required
oxds.setTNSEntryName("ora805");
...

ThinネイティブXA

JDBC OCIドライバと同様に、JDBC THINドライバもネイティブXAをサポートしています。ただし、JDBC THINドライバはデフォルトでネイティブXAをサポートします。一方、JDBC OCIドライバの場合、ネイティブXAのサポートがデフォルトでは使用可能になりません。

次のようにXAデータソースでsetNativeXA(false)をコールすることで、ネイティブXAを無効にできます。

...
// Create a XADataSource instance
OracleXADataSource oxds = new OracleXADataSource();
...
// Disabling Native XA
oxds.setNativeXA(false);
...

たとえば、ネイティブXAコードの不具合に対する回避策として、ネイティブXAの無効化が必要になる場合があります。