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.TMRESUMExidで指定したトランザクション・ブランチを再開します。ノート:
再開できるのは、前に保留されていたトランザクション・ブランチのみです。
-
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.TMSUSPENDxidで指定したトランザクション・ブランチを保留することを示します。トランザクション・ブランチを保留することにより、複数のトランザクション・ブランチを単一セッションで使用できます。ただし、常にアクティブにできるのは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の無効化が必要になる場合があります。