32 分散トランザクション
この章では、分散トランザクションのOracle Java Database Connectivity(JDBC)実装について説明します。これは多フェーズ・トランザクションで、しばしば複数のデータベースを使用し、協調してコミットされる必要があります。Java固有でなく分散トランザクションの一般的な標準であるXAについての関連説明もあります。
次の内容について説明します。
ノート:
この章では、javax
パッケージで使用可能な、JDBC 2.0 Standard Extension Application Program Interface (API)と呼ばれる、JDBC 2.0 Optional Packageの機能について説明します。
分散トランザクションの詳細および一般情報は、JDBC 2.0 Optional PackageおよびJava Transaction API(JTA)の仕様を参照してください。
32.1 分散トランザクションについて
この項の内容は次のとおりです:
32.1.1 分散トランザクションの概要
分散トランザクション(グローバル・トランザクションとも呼ばれる)は、複数の関連トランザクションの組合せで、協調して管理する必要があります。分散トランザクションを構成するトランザクションは、同じデータベース内に存在することもありますが、通常は異なるデータベースに存在し、多くの場合は異なる場所に存在します。分散トランザクションを構成する各トランザクションをトランザクション・ブランチと呼びます。
たとえば、分散トランザクションには、ある銀行のある口座から、別の銀行の口座への送金があります。両方の処理が正常に完了する保証がなければ、トランザクションはコミットできません。
JDBCでは、分散トランザクション機能が接続プーリング機能の最上位に構築されます。この分散トランザクション機能は、分散トランザクションのオープンXA標準にも基づいて構築されます。XAはX/Open標準の一部で、Java固有ではありません。
データベース・リソースに接続するには、JDBCを使用します。ただし、複数のデータベースに対するすべての変更を1回のトランザクションで更新するには、JTAグローバル・トランザクションでJDBC接続を使用する必要があります。トランザクションにデータベースSQL更新を含める処理は、データベース・リソースの参加と呼ばれます。
32.1.2 分散トランザクションのコンポーネントおよびシナリオ
この項の以降の部分を読むには、次のポイントを覚えておくと役に立ちます。
-
分散トランザクション・システムは、通常、標準JTA機能を実装するソフトウェア・コンポーネントなど、外部のトランザクション・マネージャを使用して、個々のトランザクションを協調させます。
多くのベンダーが、XA準拠のJTAモジュールを提供しています。たとえば、Oracleからは、Oracle9i Application ServerおよびOracle Application Server 10gでJTAが提供されています。
-
XA機能は、通常、クライアント・アプリケーションから孤立しています。クライアント・アプリケーションではなく、アプリケーション・サーバーなど、中間層環境に実装されます。
多くの場合、アプリケーション・サーバーとトランザクション・マネージャはともに中間層に位置します。同時に、アプリケーション・コードの一部も中間層に位置します。
-
この項の説明は、主に、中間層の開発者を対象にしています。
-
分散トランザクションの説明では、リソース・マネージャという用語が頻繁に使用されます。リソース・マネージャとは、単に、データまたはその他のリソースを管理するエンティティです。この章では、データベースの意味で使用されています。
ノート:
JTA機能を使用するには、
CLASSPATH
環境変数にjta.jar
が含まれている必要があります。このファイルは、ORACLE_HOME
/jlib
にあります。このファイルはJDBC製品に含まれています。
32.1.3 分散トランザクションの概念
XA機能を使用する場合、トランザクション・マネージャが、XAリソース・インスタンスを使用して各トランザクション・ブランチを準備し、協調させ、すべてのトランザクション・ブランチを適切にコミットまたはロールバックします。
XA機能には、次のキー・コンポーネントが含まれています。
-
接続プーリング・データソースおよびその他のデータソースの拡張機能で、概念および機能の面で似ています。
分散トランザクションで使用されるリソース・マネージャごとに1つのXAデータソース・インスタンスがあります。通常、XAデータソース・インスタンスは、中間層ソフトウェアで作成します。
XAデータソースは、XA接続を作成します。
-
プーリングされた接続の拡張機能で、概念および機能の面で似ています。XA接続は、物理的なデータベース接続をカプセル化します。個々の接続インスタンスは、これら物理接続の一時的なハンドルです。
1つのXA接続インスタンスは、1つのOracleセッションに対応します。ただし、1つのセッションは、プーリングされた接続インスタンスのように、複数の論理接続インスタンスで順に使用できます。
通常、XA接続インスタンスは、XAデータソース・インスタンスから、中間層ソフトウェアで取得します。分散トランザクションが、同じデータベースの複数セッションにかかわる場合、単一XAデータソース・インスタンスから複数のXA接続インスタンスを取得できます。
XA接続は、
OracleXAResource
インスタンスおよびJDBC接続インスタンスを作成します。 -
分散トランザクションのトランザクション・ブランチを協調させるために、トランザクション・マネージャで使用されます。
通常、
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コンポーネントが含まれています。これにより、ブランチが分散トランザクションに関連付けられます。ある分散トランザクションに関連付けられた
OracleXAResource
インスタンスにはすべて、同じ分散トランザクションIDコンポーネントが含まれているトランザクションIDが設定されます。 -
OracleXAResource.ORATRANSLOOSE
トランザクションIDが
xid
の疎結合トランザクションを起動します。
32.1.4 ローカル・トランザクションとグローバル・トランザクションの切替えについて
アプリケーションは、ローカル・トランザクションとグローバル・トランザクションの間で接続を共有できます。また、アプリケーションは、ローカル・トランザクションとグローバル・トランザクションの間で接続を切り替えることもできます。
接続は必ず次のモードのいずれかです。
-
NO_TXN
この接続をアクティブの状態で使用しているトランザクションはありません。
-
LOCAL_TXN
自動コミットがオフまたは無効になっているローカル・トランザクションが、アクティブの状態でこの接続を使用しています。
-
GLOBAL_TXN
グローバル・トランザクションが、アクティブの状態でこの接続を使用しています。
各接続では、その接続で実行されている操作によって、これらのモードが自動的に切り替わります。接続は、インスタンス化されたときは常にNO_TXN
モードです。
ノート:
各モードはOracle Databaseに関連付けられたJDBCドライバによって内部的に保持されます。
表32-1では、接続モードの推移のルールについて説明します。
表32-1 接続モードの推移
現在のモード | NO_TXNに切り替わるとき | LOCAL_TXNに切り替わるとき | GLOBAL_TXNに切り替わるとき |
---|---|---|---|
|
N/A |
自動コミット・モードがfalseで、Oracle Data Manipulation Language(DML)文が実行された場合 |
現在の接続を提供した |
|
次のいずれかの場合
|
N/A |
現在の接続を提供した |
|
この接続でオープンしているグローバル・トランザクション内で、この接続を提供した |
なし |
N/A |
前述のルールのいずれにも該当しない場合、モードは変わりません。
操作に関するモードの制限
現在の接続モードによって、トランザクション内で有効な操作が制限されます。
-
LOCAL_TXN
モードの場合、アプリケーションはprepare
、commit
、rollback
、forget
またはend
をXAResource
でコールできません。これらを起動すると、XAException
が発生します。 -
GLOBAL_TXN
モードの場合、アプリケーションではcommit
、rollback
、rollback(Savepoint)
、setAutoCommit(true)
またはsetSavepoint
をjava.sql.Connection
でコールすることや、OracleSetSavepoint
またはoracleRollback
をoracle.jdbc.OracleConnection
でコールすることはできません。これらを起動すると、SQLException
が発生します。ノート:
このモードに関する制限のエラー・チェックは、トランザクションとセーブポイントAPIに関する標準のエラー・チェックに追加されます。
32.1.5 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
パッケージにあります。それらの機能が、コードが実行される場所に依存しないからです。
中間層のシナリオでは、OracleXid
、OracleXAException
およびoracle.jdbc.xa.client
パッケージをインポートします。
ただし、XAコードをターゲットOracle Databaseで実行する場合は、oracle.jdbc.xa.client
パッケージのかわりに、oracle.jdbc.xa.server
パッケージをインポートします。
ターゲット・データベースの内部で実行するコードでリモート・データベースにもアクセスする必要がある場合は、どちらのパッケージもインポートしません。かわりに、client
パッケージから使用するクラス(リモート・データベースにアクセスするとき)またはserver
パッケージから使用するクラス(ローカル・データベースにアクセスするとき)の名前を完全に修飾する必要があります。クラス名は、これらのパッケージで重複しています。
32.2 XAコンポーネント
この項では、XAコンポーネント、つまりJDBC標準で指定される標準XAインタフェースと、それを実装するOracleクラスについて説明します。内容は次のとおりです。
32.2.1 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)に登録できます。
関連項目:
高速接続フェイルオーバーの詳細は、『Oracle Universal Connection Pool for JDBC開発者ガイド』を参照してください。
32.2.2 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
インタフェースを拡張します。そのため、getConnection
、close
、addConnectionEventListener
および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
メソッドをコールすると、データベースへの物理接続がクローズされます。これは、通常、中間層で実行します。
32.2.3 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
インスタンスを関連付けるときのメソッドです。
32.2.4 OracleXAResourceメソッドの機能と入力パラメータ
OracleXAResource
クラスには、トランザクション・ブランチを、関連付けられた分散トランザクションと協調させるためのメソッドがいくつかあります。この機能は、通常、2フェーズ・コミット操作で起動されます。
通常、トランザクション・マネージャが、アプリケーション・サーバーなどの中間層コンポーネントからOracleXAResource
インスタンスを受け取り、この機能を起動します。
これらのメソッドはそれぞれ、Xid
インスタンスの形で、トランザクション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.TMPROMOTE
ローカル・トランザクションからグローバル・トランザクションへ昇格します
-
OracleXAResource.ORATMSERIALIZABLE
トランザクションIDが
xid
のシリアライズ可能なトランザクションを起動します。 -
OracleXAResource.ORATMREADONLY
トランザクションIDが
xid
の読取り専用トランザクションを起動します。 -
OracleXAResource.ORATMREADWRITE
トランザクションIDが
xid
の読取り/書込みトランザクションを起動します。 -
OracleXAResource.ORATRANSLOOSE
トランザクションIDが
xid
の疎結合トランザクションを起動します。
TMNOFLAGS
、TMJOIN
、TMRESUME
、TMPROMOTE
、ORATMSERIALIZABLE
、ORATMREADONLY
およびORATMREADWRITE
は、XAResource
インタフェースおよびOracleXAResource
クラスのstatic
メンバーとして定義されています。ORATMSERIALIZABLE
、ORATMREADONLY
および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: フェッチ順序が無効です。」
の発生を防ぐには、start
メソッドの一部としてTMNOMIGRATE
フラグを含めます。次に例を示します。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つのセッションを使用するよりもコストがかかります。
TMSUCCESS
、TMFAIL
および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: フェッチ順序が無効です。」
の発生を防ぐには、endメソッドの一部としてTMNOMIGRATE
フラグを含めます。次に例を示します。end(xid, XAResource.TMSUSPEND | OracleXAResource.TMNOMIGRATE);
-
OracleXAResource
で定義されているフラグはすべてOracle拡張機能です。これらのフラグを使用するトランザクション・マネージャの場合は、この点を留意してください。
prepare
xid
で指定されたトランザクション・ブランチで実行される変更の準備をします。これは、データベースへのアクセスと、変更の正常なコミットを保証する、2フェーズ・コミット操作の最初のフェーズです。
int prepare(Xid xid)
このメソッドでは、次の整数値を戻します。
-
XAResource.XA_RDONLY
トランザクション・ブランチが、
SELECT
文など、読取り専用操作のみを実行する場合に戻されます。 -
XAResource.XA_OK
トランザクション・ブランチが更新を実行する場合、すべて準備済で、エラーがなければ、この値が戻されます。
XA_RDONLY
およびXA_OK
は、XAResource
インタフェースおよびOracleXAResource
クラスのstatic
メンバーとして定義されています。
ノート:
-
prepare
メソッドでは、トランザクション・ブランチが更新を実行する場合、そのうち1つでも準備中にエラーが発生すると、値が戻されないことがあります。この場合、XA例外がスローされます。 -
prepare
メソッドをコールする前に、必ずブランチのend
メソッドをコールする必要があります。 -
分散トランザクションにあるトランザクション・ブランチが1つのみの場合、
prepare
メソッドをコールする必要はありません。準備なしに、OracleXAResource
のcommit
メソッドをコールできます。
commit
xid
で指定したトランザクション・ブランチで準備された変更をコミットします。これは2フェーズ・コミットの2番目のフェーズで、トランザクション・ブランチをすべて正常に準備できた場合のみ実行されます。
void commit(Xid xid, boolean onePhase)
onePhase
パラメータは、次のように設定します。
-
true
トランザクション・ブランチをコミットするときに、2フェーズ・プロトコルでなく1フェーズ・プロトコルを使用します。これは、分散トランザクションにトランザクション・ブランチが1つのみ存在する場合に適しています。
prepare
ステップは省略されます。 -
false
トランザクション・ブランチをコミットするときに、2フェーズ・プロトコルを使用します。
rollback
xid
で指定したトランザクション・ブランチで準備された変更をロールバックします。
void rollback(Xid xid)
forget
ヒューリスティックに決着されたトランザクション・ブランチを無視するようにリソース・マネージャに通知します。
public void forget(Xid xid)
recover
トランザクション・マネージャは回復時にこのメソッドを呼び出して、現在準備状態またはヒューリスティックに決着された状態にあるトランザクション・ブランチのリストを取得します。
public Xid[] recover(int flag)
ノート:
TMSTARTRSCAN
、TMENDRSCAN
またはTMNOFLAGS
以外の値をflag
に使用すると、例外が発生します。そうでない場合、flag
は無視されます。
リソース・マネージャは、現在準備中またはヒューリスティックに完了した状態のトランザクション・ブランチに対して、0(ゼロ)個以上のXid
を戻します。操作中にエラーが発生した場合は、リソース・マネージャにより適切なXAException
が発行されます。
ノート:
recover
メソッドは、Oracle DatabaseサーバーのDBA_PENDING_TRANSACTIONS
上でSELECT権限、SYS.DBMS_XA
上でEXECUTE権限が必要です。Oracle Database 11gリリース1より前のデータベース・バージョンでは、Oracle Bug#5945463の修正を含むOracleパッチが利用できないか、個別のアプリケーション使用例にそのパッチを適用できない場合、recover
メソッドは、SYSBDBA
権限も必要とします。SYSDBA
権限を定期的に使用すると、セキュリティ上の問題があります。したがって、recover
メソッドを使用する必要がある場合、データベースをアップグレードするか、またはBug#5945463の修正を適用することを強くお薦めします。
isSameRM
2つのOracleXAResource
インスタンスが同じリソース・マネージャに対応しているかどうかを判断するには、一方のOracleXAResource
インスタンスから、入力としてもう一方のOracleXAResource
インスタンスを指定して、isSameRM
メソッドをコールします。次の例では、xares1
およびxares2
はOracleXAResource
インスタンスとします。
boolean sameRM = xares1.isSameRM(xares2);
32.2.5 XidインタフェースとOracle実装
トランザクション・マネージャは各トランザクション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()
32.3 エラー処理と最適化
この項では、XA例外の機能とエラー処理およびXA実装でのOracle最適化について説明します。内容は次のとおりです。
例外とエラー処理の説明には、標準XA例外クラスとOracle固有のXA例外クラスが含まれます。また、特定のXAエラー・コードおよびエラー処理の方法も含まれます。
32.3.1 XAExceptionクラスとメソッド
XAメソッドでは、一般例外やSQLExceptions
ではなく、XA例外が発生します。XA例外は、標準クラスjavax.transaction.xa.XAException
またはそのサブクラスのインスタンスです。
Oracle XAExceptionは、Oracleエラーの部分とXAエラーの部分で構成されています。Oracleには、標準javax.transaction.xa.XAException
クラスのoracle.jdbc.xa.OracleXAException
サブクラスが用意されています。OracleXAException
インスタンスは、次のコンストラクタの1つを使用して構成されます。
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
クラスで定義されています。
32.3.2 OracleエラーとXAエラーのマッピング
表32-2で示すように、OracleエラーはOracleXAException
インスタンスのXAエラーに対応しています。
表32-2 OracleとXAのエラー・マッピング
Oracleエラー・コード | XAエラー・コード |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
その他すべての |
|
32.3.3 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例外の処理を行いません。
32.3.4 Oracle XA最適化
Oracle JDBCには、分散トランザクションの2つ以上のブランチが同じデータベース・インスタンスを使用する場合、つまり、これらのブランチに関連付けられたOracleXAResource
インスタンスが同じリソース・マネージャに関連付けられている場合、パフォーマンスを改善する機能があります。
このような場合、これらのOracleXAResource
インスタンスのうち1つのprepare
メソッドのみがXA_OK
を戻すか、または失敗します。残りは、更新が行われる場合でも、XA_RDONLY
を戻します。これにより、トランザクション・マネージャは、すべてのトランザクション・ブランチを暗黙的に結合し、XA_OK
を戻した(または失敗した)OracleXAResource
インスタンスによって、結合されたトランザクションをコミット(失敗した場合はロールバック)できます。
トランザクション・マネージャは、OracleXAResource
クラスのisSameRM
メソッドを使用して、2つのOracleXAResource
インスタンスが同じリソース・マネージャを使用しているかどうかを判断できます。このようにして、XA_RDONLY
戻り値の意味を解析できます。
32.4 分散トランザクションの実装について
この項では、Oracle XA機能を使用して分散トランザクションを実装する方法の例を示します。この項の内容は次のとおりです。
32.4.1 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
パッケージには、それぞれのバージョンのOracleXADataSource
、OracleXAConnection
およびOracleXAResource
クラスがあります。これら3つのクラスの抽象バージョンは、最上位のoracle.jdbc.xa
パッケージに含まれています。
32.4.2 OracleのXAコード・サンプル
このサンプルでは、異なるデータベースに対する2つのトランザクション・ブランチで、2フェーズ分散トランザクションを使用します。
簡単にするため、このサンプルでは、通常は中間層に置くコードと、通常はトランザクション・マネージャに置くコード(OracleXAResource
メソッドの起動や、トランザクションIDの作成など)を組み合せているので、注意してください。
短くするため、トランザクションID作成の指定およびSQL操作の実行は、ここでは示しません。完全な例は製品に付属しています。
このサンプルは、次の順序で実行します。
-
トランザクション・ブランチ1を開始します。
-
トランザクション・ブランチ2を開始します。
-
ブランチ1のDML操作を実行します。
-
ブランチ2のDML操作を実行します。
-
トランザクション・ブランチ1を終了します。
-
トランザクション・ブランチ2を終了します。
-
ブランチ1を準備します。
-
ブランチ2を準備します。
-
ブランチ1をコミットします。
-
ブランチ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=localhost) (protocol=tcp)(port=5521))(connect_data=(service_name=orcl)))"; // Create first DataSource and get connection OracleDataSource ods1 = new OracleDataSource(); ods1.setURL(URL1); ods1.setUser("HR"); ods1.setPassword("hr"); Connection conna = ods1.getConnection(); // Create second DataSource and get connection OracleDataSource ods2 = new OracleDataSource(); ods2.setURL(URL2); ods2.setUser("HR"); ods2.setPassword("hr"); 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("HR"); oxds1.setPassword("hr"); OracleXADataSource oxds2 = new OracleXADataSource(); oxds2.setURL("jdbc:oracle:thin:@(description=(address=(host=localhost) (protocol=tcp)(port=5521))(connect_data=(service_name=orcl)))"); oxds2.setUser("HR"); oxds2.setPassword("hr"); // 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...} }
32.5 Oracle JDBCドライバのネイティブXA
通常、XAコマンドは次の方法でサーバーに送信できます。
-
非ネイティブAPIの使用
-
ネイティブAPIの使用
この2つの方法でサーバーにXAコマンドを送信する場合、パフォーマンスに大きな相違があります。非ネイティブAPIと比較して、ネイティブAPIを使用するほうが、高いパフォーマンスを達成できます。
Oracle Database 10gより前は、ThinネイティブのAPIが利用できなかったため、ThinドライバはネイティブでないAPIを使用してXAコマンドをサーバーに送信していました。ネイティブでないAPIはPL/SQLプロシージャを使用します。このため、次の短所があります。
-
ワイヤ上で異なるメッセージが必要です。
-
データベースとのラウンドトリップが増加します。
-
オープン状態のカーソルが増加します。
-
文キャッシュの領域を占有することによって、文キャッシュを破損します。
さらに、非ネイティブAPIの実装がサーバー内にあります。したがって、XAコマンドを送信する際の問題を解決するために、サーバーのパッチが必要です。パッチを適用するとサーバーを再起動する必要があるので、大きな問題になります。
Oracle Database 10gから、ThinネイティブAPIを使用できるようになったため、デフォルトではこのAPIを使用してXAコマンドを送信します。ネイティブAPIは、非ネイティブAPIよりも処理が10倍高速です。
この項の内容は次のとおりです。
32.5.1 OCIネイティブXA
ネイティブXAは、OracleXADataSource
クラスのtnsEntry
およびnativeXA
プロパティを使用して有効にします。
ノート:
現在のところ、OCI Native XAは、マルチスレッド環境では機能しません。OCIドライバは、OracleのC/XAライブラリを使用して分散トランザクションをサポートします。このため、グローバル・トランザクションを再開する前に、各スレッドごとにXAConnectionを取得する必要があります。
構成およびインストール
SolarisまたはLinuxシステムの場合、ネイティブXA機能を使用可能にするには、libheteroxa11.so
共有ライブラリが必要です。ネイティブXA機能を正しく動作させるには、このライブラリをインストールして、検索パスで使用可能にする必要があります。
Microsoft Windowsシステムの場合、ネイティブXA機能を使用可能にするには、heteroxa11.dll
ファイルが必要です。ネイティブXA機能を正しく動作させるには、このファイルをインストールして、WindowsのDLLパスで使用可能にする必要があります。
例外処理
分散トランザクションでネイティブXA機能を使用する場合は、アプリケーションでOracleXAException
またはOracleSQLException
をチェックするのではなく、単純にXAException
またはSQLException
をチェックすることをお薦めします。
ノート:
標準の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"); ...
関連トピック
32.5.2 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の無効化が必要になる場合があります。