11 パフォーマンスとデバッグ
この章では、SQLJアプリケーションのパフォーマンスを向上させ、SQLJのソース・コードを実行時にデバッグするための機能、ユーティリティおよびヒントについて説明します。次の内容について説明します。
11.1 パフォーマンス強化の機能
Oracle SQLJ実装には、データへのアクセスを効率化することによって、パフォーマンスを向上させるための機能が提供されています。次のようなものがあります:
デフォルトのOracle固有コード生成の使用は、アプリケーションにとってメリットがあります。生成されたコードはOracle Java Database Connectivity(JDBC)ドライバへの直接コールによって最適化されるため、(JDBCへのコール前の)SQLJランタイムへの中間コールのオーバーヘッドが削減できます。
ノート:
行のセットを値の配列にフェッチするバッチ・フェッチは、Oracle SQLJ実装でサポートされていません。ただし、Oracle行プリフェッチを使用すると、バッチ・フェッチのメリットをいくつか得られます。
前述したSQLJパフォーマンス強化に加えて、SQLJプログラムではSQL操作に関するオプティマイザ・ヒントを使用できます。同様に、他のOracle SQL操作に関するヒントも使用できます。
Oracle SQL実装では、「/*+」や「--+」で始まるコメント表記法でOracle SQLオプティマイザにヒントを渡すことによって、SQL文をチューニングすることができます。SQLJトランスレータは、これらのオプティマイザ・ヒントを認識してそれらをサポートし、実行時にSQL文の一部として渡します。
Oracle Database 12c リリース2 (12.2)のSQLの最適化のための拡張機能を使用すると、SQLJストアド・ファンクションやその他のストアド・ファンクションのコストおよび選択性の情報を定義できます。オプティマイザを使用すると、SQL実行時に、ストアド・ファンクションのコスト関数および選択関数の起動、他の実行方法の評価、ならびに最も効率的な方法の選択ができます。
関連項目:
詳細は、『Oracle Database SQL言語リファレンス』を参照してください。
コード中にOracleパフォーマンス拡張機能を使用する場合は、次の要件が伴うので注意してください。
-
Oracle JDBCドライバを使用する必要があります。
-
デフォルトのOracle固有コード生成を使用するか、プロファイルを適切にカスタマイズします。
ISO標準コード生成の場合は、デフォルトのカスタマイザ
oracle.sqlj.runtime.util.OraCustomizerをお薦めします。 -
アプリケーションの実行時にOracle SQLJランタイムを使用します。
Oracle SQLJランタイムとOracle JDBCドライバは、Oracleカスタマイザを使用してプロファイルをカスタマイズするときは常にアプリケーションで必要となるもので、Oracle拡張機能をコード中に使用しない場合にもこのことが当てはまります。
11.1.1 行プリフェッチ
標準のJDBCは、問合せ結果を1行ずつ受け取り、行ごとにデータベースへの別個のラウンドトリップを必要とします。行プリフェッチを使用すると、一度に複数行の結果を効率的に受け取ることができます。
ExecutionContextインスタンスのsetFetchSize()メソッドを使用して、SELECT文(特定のExecutionContextインスタンスを使用するSQLJ文用)を実行するたびにプリフェッチされる行数を設定します。
ExecutionContextインスタンスのgetFetchSize()メソッドは、現行のプリフェッチ・サイズをint値として戻します。
次の例では、デフォルトの接続コンテキスト・インスタンスのデフォルトの実行コンテキスト・インスタンスを取得し、setFetchSize()メソッドをコールして、プリフェッチ・サイズを20に設定します。
DefaultContext.getDefaultContext().getExecutionContext().setFetchSize(20);
JDBC Application Programming Interface(API)を使用して、基になるOracleConnectionオブジェクトに直接プリフェッチ・サイズを設定することも可能ですが、SQLJ内ではこれは設定できません。
特定の接続コンテキストのインスタンスを使用した問合せに対して、プリフェッチする行数を指定するには、基になるJDBC接続を使用してOracleConnectionインスタンスにキャストします。次の例は、デフォルト接続で、プリフェッチする行数を20に設定しています。
((OracleConnection)DefaultContext.getDefaultContext().getConnection()).setDefaultRowPrefetch(20);
また、SQLJ接続コンテキストで設定されたプリフェッチ・サイズが、基になるJDBC接続で設定されたプリフェッチ・サイズより優先されることに注意してください。
使用する各追加接続コンテキスト・インスタンスは、必要に応じて個別に設定する必要があります。プリフェッチの値は、接続コンテキストごとに設定する必要があります。たとえば、宣言された接続コンテキスト・クラスのctxというインスタンスに対しては、プリフェッチする行数を次のように設定します。
((Connection)ctx.getConnection()).setDefaultRowPrefetch(20);
ctx.getExecutionContext().setFetchSize(20);
プリフェッチできる行数に制限はありません。JDBCでのデフォルト値は10で、この値はSQLJに継承されます。この値は通常の環境では効果的ですが、受け取る行数が多い場合はこの値を大きくできます。
11.1.2 文のキャッシング
SQLJには文のキャッシング機能が用意されており、繰り返し使用される実行文(ループ内や、繰り返しコールされるメソッド内などの実行文)を保存することでパフォーマンスが向上します。再実行の前に文がキャッシュされると、コードの再解析(クライアントまたはサーバーで)、文オブジェクトの再作成、およびパラメータ・サイズの定義の再計算が不要になります。この機能がない場合、繰り返し使用される文を、クライアント上で(おそらくサーバー内でも)再解析する必要があります(この再解析の必要性は、文の再検出時に、その文がサーバー側のSQLキャッシュでまだ使用可能かどうかによります)。
Oracle固有コード生成の場合、SQLJの文キャッシングはOracle JDBCドライバに依存し、このドライバは、JDBCの明示的なキャッシング・メカニズムを使用します。後述のような相互依存性がありますが、この明示的なキャッシング・メカニズムは、JDBCの暗黙的なキャッシング・メカニズムとは異なります。Oracle固有のコードでは、文のキャッシングは接続メソッドを介して制御されます。
ISOコード生成の場合、SQLJには、SQLJランタイムの機能を介したSQLJ自体の文キャッシング・メカニズムがあります。ISOコードでは、文のキャッシングはOracleカスタマイザのstmtcacheオプションを介して制御されます。
ノート:
Oracle固有コード生成の場合は、明示的なキャッシングがSQLJのAPIを介して操作できる唯一の文キャッシング・メカニズムです。このマニュアルでは、このメカニズムをSQLJ/明示的な文キャッシングと呼びます。
Oracle Database 12c リリース1 (12.1)では、JDBC接続が接続コンテキストによって作成される場合、デフォルトの文キャッシュ・サイズが5に設定されます。接続コンテキストが既存の使用可能なJDBC接続またはデータ・ソースを使用して作成された場合、文のキャッシュ・サイズはそのJDBC接続またはデータ・ソースのキャッシュ・サイズに設定されます。
文キャッシングのための接続コンテキスト・メソッド(Oracle固有コードの場合)
Oracle固有コード生成を使用する場合(SQLJトランスレータのデフォルトの-codegen=oracle設定を使用する場合)は、文キャッシング機能に接続コンテキスト・メソッドを使用します。文のキャッシュ・サイズに0(ゼロ)よりも大きい値を指定することによって、SQLJ/明示的な文キャッシングが有効になります。デフォルトの場合、キャッシュ・サイズが5(5文)で有効となります。
次のOracle固有(非標準)staticメソッドがsqlj.runtime.ref.DefaultContextクラスに追加されました。また、宣言した接続コンテキスト・クラスには、これらのメソッドが含まれています。
-
static void setDefaultStmtCacheSize(int)デフォルトの文キャッシュ・サイズをすべての接続コンテキストに設定します。これは、このメソッドをコールしたクラスだけでなく、これ以降に作成されるすべての接続コンテキスト・クラスのインスタンスに使用される、初期の文キャッシュ・サイズになります。このメソッド・コールは、既存の接続コンテキスト・インスタンスには影響しません。
ノート:
setDefaultStmtCacheSize(int)は、SQLJ接続コンテキストを使用して作成した接続の文キャッシュ・サイズにのみ影響します。JDBC接続を使用して作成した接続の文キャッシュ・サイズには影響しません。次の2つのコード例を考えてみます。
例1:
... MyContext.setDefaultStmtCacheSize(10); OracleConnection conn = DriverManager.getConnection(url, user, passwd); myctx = new MyContext(conn);
例2:
... MyContext.setDefaultStmtCacheSize(10); myctx = new MyContext(url, user, passwd,true);
前述の2つの例では、2番目の例の文キャッシュ・サイズのみが10に設定されます。1番目の例では、JDBC仕様の
DriverManagerインタフェースのgetConnectionメソッドを使用して接続が作成されたため、この接続に対応する文キャッシュ・サイズは影響を受けません。 -
static int getDefaultStmtCacheSize()接続コンテキストに対する現行のデフォルトの文キャッシュ・サイズを取得します。
また、次のOracle固有インスタンス・メソッドがDefaultContextクラスに追加されました。これらのメソッドは、他のどの接続コンテキスト・クラスにも含まれています。
-
void setStmtCacheSize(int) throws java.sql.SQLException特定の接続コンテキスト・インスタンスの基になる接続に文キャッシュ・サイズを設定します(デフォルトを上書きします)。
ノート:
SQLJ/明示的なキャッシングがすでに無効な場合は、サイズを0(ゼロ)に設定すると無効のままになります。すでに有効な場合にサイズを0(ゼロ)に設定すると、有効のままですが機能は停止します。
-
int getStmtCacheSize()SQLJ/明示的な文キャッシングが、接続コンテキストの基になる接続に対して有効かどうかを検証します。有効な場合は、現行の文キャッシュ・サイズを戻します。また、次の整定数のいずれかを戻すこともできます。
static int STMT_CACHE_NOT_ENABLED static int STMT_CACHE_EXCEPTION
getStmtCacheSize()コールでSQL例外が発生する可能性があります。ただし、下位互換性のため、このメソッドでは例外を直接スローすることはありません。例外が発生した場合、このメソッドは定数STMT_CACHE_EXCEPTIONを戻します。この場合は、getStmtCacheException()メソッドをコールして、例外の内容を確認できます。SQLJ/明示的なキャッシングが無効に設定されている場合に
getStmtCacheSize()をコールすると、定数STMT_CACHE_NOT_ENABLEDが戻されます。これはキャッシュ・サイズが0(ゼロ)であることとは区別されます。0(ゼロ)のキャッシュ・サイズでSQLJ/明示的なキャッシングを有効にする(役には立ちませんが)ことは、技術的には可能です。 -
java.sql.Exception getStmtCacheException()文キャッシングの例外があるかどうかを確認します。このメソッドの使用方法は2種類あります。
-
getStmtCacheSize()コールでSTMT_CACHE_EXCEPTIONが戻される場合にコールします。 -
文のキャッシングを使用するための接続コンテキスト・インスタンスを作成する場合は、必ずこのメソッドをコールします。これは、接続コンテキスト・インスタンスの作成時に、常に文キャッシュ・サイズに関して発生する自動操作のためのコールです。接続コンテキスト・インスタンスに対して文のキャッシングを行う場合は、インスタンスの作成後に
getStmtCacheException()をコールして、問題がなかったことを確認します。
-
文キャッシングの有効化と無効化(Oracle固有コードの場合)
Oracle固有コードでは、前に実行された文を繰り返すと、0(ゼロ)でない文キャッシュ・サイズによってSQLJ/明示的なキャッシングが有効になります。デフォルト・サイズが5のため、文のキャッシングがデフォルトで有効になっています。
SQLJ/明示的な文キャッシングは、SQLJのAPIを介して明示的に無効にすることはできませんが、文キャッシュ・サイズを0 (ゼロ)に設定することで、事実上無効(機能停止)にできます。この場合、接続コンテキストのgetStmtCacheSize()メソッドは、STMT_CACHE_NOT_ENABLEDではなく0 (ゼロ)を戻す場合があります。
SQLJ/明示的な文キャッシングまたはJDBCの暗黙的なキャッシングは、JDBC接続APIを介して明示的に無効にできます。SQLJ/明示的なキャッシングおよびJDBCの暗黙的なキャッシングでは、同じキャッシュ・サイズを使用していることが、明示的に無効にする理由の一部である場合があります。次のメソッドは、OracleConnectionクラスを介して使用できます。
-
void setExplicitCachingEnabled(boolean) -
boolean getExplicitCachingEnabled() -
void setImplicitCachingEnabled(boolean) -
boolean getImplicitCachingEnabled()
SQLJ接続コンテキスト・インスタンス内からOracleConnectionインスタンスを取得すると、これらのメソッドにアクセスできます。
ノート:
SQLJでは、JDBCの暗黙的なキャッシングはデフォルトで無効です。これはsetImplicitCachingEnabled()メソッドを介して明示的に有効にしないかぎり無効のままです。
SQLJ/明示的なキャッシングとJDBCの暗黙的なキャッシング間の主な相互作用
Oracle固有コードでの文のキャッシングに関して、このマニュアルでは、JDBCの暗黙的なキャッシングではなく、SQLJ/明示的なキャッシングに説明の重点をおいています。アプリケーションでJDBCコードを使用しない場合は、SQLJ/明示的なキャッシングが唯一の適切な文のキャッシングです。ただし、SQLJとJDBCコードの両方をアプリケーションで使用する環境では、暗黙的なキャッシングも使用する可能性があります。
SQLJ/明示的なキャッシングとJDBCの暗黙的なキャッシングは、相互に関係なく有効です。さらに、ユーザーはSQLJを介して暗黙的なキャッシュにアクセスすることはできません。ただし、この2種類のキャッシング間には、同じキャッシュ・サイズを共有するという主な相互作用があります。たとえば、文のキャッシュ・サイズが5の場合は、SQLJ/明示的なキャッシングと暗黙的なキャッシングの組合せで最大5個の文をキャッシュできます。
ここで重要な点は、キャッシュ・サイズを0(ゼロ)に設定して、SQLJ/明示的な文キャッシングを無効に設定すると、暗黙的なキャッシングも無効になることです。
また、SQLJ/明示的なキャッシングが無効な場合に、キャッシュ・サイズを0(ゼロ)より大きい値に変更するとそのキャッシングが有効になりますが、これは暗黙的なキャッシングの有効化には影響を与えません。
文キャッシングに対するJDBCサポート(ISOコードの場合)
SQLJトランスレータに-codegen=isoを設定して指定されたISO標準コード生成の場合、文キャッシングは標準SQLJ機能であり、特定のJDBCドライバは不要です。ただし、sqlj.runtime.profile.ref.ClientDataSupportインタフェースを実装したドライバを使用すると、キャッシングの堅牢性が向上します。Oracle Database 12c リリース1 (12.1) JDBCドライバには、このインタフェースが実装され、次の機能が提供されています。
-
アプリケーション全体で静的キャッシュを1つのみ使用するのではなく、データベース接続のたびに別個のキャッシュを使用できます。
-
同じ接続からの複数の接続コンテキスト・クラスのインスタンスの間でキャッシュされた文を共有できます。
キャッシュを1つ使用した場合、ClientDataSupportが実装されていない汎用JDBCドライバの場合と同様、一方の接続で文を実行したために、もう一方の接続で実行した文がフラッシュされてしまうことがあります(文のキャッシュ・サイズ、つまりキャッシングの許容される文の上限の数が超過した場合)。
文のキャッシュ・サイズに対応したOracleカスタマイザ・オプション(ISOコードの場合)
ISO標準コード生成の場合、文のキャッシングはOracleカスタマイザの使用時にアプリケーション内で有効です。デフォルトのキャッシュ・サイズは5(Oracle固有コードと同じデフォルトのサイズ)です。文のキャッシングは通常、Oracle SQLJ変換の一部として実行されます。
必要に応じて文のキャッシュ・サイズを変更したり、キャッシュ・サイズを0(ゼロ)に設定して文のキャッシングを無効にするには、Oracleカスタマイザのstmtcacheオプションを使用できます。-P-Cstmtcache=nのように設定します(nは整数)。
接続コンテキスト・クラスをいくつか使用するに伴ってプロファイルもいくつか使用する場合は、プロファイルごとに個別にSQLJ(事実上はカスタマイザ)を実行すると、それぞれの文のキャッシュ・サイズを個別に設定できます。
実行時、文のキャッシュ・サイズは、適切なSQLJプロファイルによって接続に対して決定されます。これは、この接続でインスタンス化された最初の接続コンテキスト・クラスに対応するプロファイルのことです。文のキャッシュ・サイズの設定値は、プロファイルのカスタマイズ時に設定したOracleカスタマイザのstmtcacheオプションの値に応じて決まります。接続時に最初の文が実行されたとき、その接続に使用する実行時の文のキャッシュ・サイズが設定されます。
文のキャッシングに関するその他の動作
SQLJ接続コンテキスト・オブジェクトがインスタンス化されるときに、基になるJDBC接続の文キャッシュ・サイズが接続コンテキスト・クラスのデフォルトのサイズよりも小さい場合、SQLJランタイムは、JDBCの文キャッシュ・サイズを接続コンテキストのデフォルト値まで増やそうとします。この操作は、ISOコード生成でも発生し、処理中の明示的な文キャッシングを有効にしますが、これは実際にはISOコードの例とは関連がありません。
反対に、実際のJDBC文キャッシュ・サイズが大きい場合は、SQLJランタイムはキャッシュ・サイズを変更しません。SQLJランタイムでは、SQLJ接続コンテキスト・インスタンスを作成すると、デフォルトのサイズ・セットに対する実際のJDBCキャッシュ・サイズを必ず確認します。
これらのメソッドは、同一の基になる静的フィールドを変更または報告するので、メソッドが発行されたコンテキスト・クラスにかかわらず、同じ効果があることに注意します。
次の接続コンテキスト・クラス宣言を例として挙げます。
#sql context CtxtA; #sql context CtxtB;
この場合、次の3つのコード命令には、それ以降に新しいSQLJ接続コンテキスト・インスタンスが作成される際に、SQLJ/明示的な文キャッシングの有効化が試行されないという効果があります。
sqlj.runtime.ref.DefaultContext.setDefaultStmtCacheSize(0);
CtxtA.setDefaultStmtCacheSize(0);
CtxtB.setDefaultStmtCacheSize(0);
ノート:
SQLJ接続コンテキスト・インスタンスが基になるJDBCのプールされた接続上に作成される場合は、SQLJはJDBC文キャッシュ・サイズを変更できません。Oracle固有コードの場合、発生した例外は接続コンテキストのgetStmtCacheException()メソッドを介して取得できます。この場合、必要なJDBC文キャッシュ・サイズを基になる物理接続に明示的に設定する必要があります。データ・ソースの場合、キャッシュ・サイズは、ベンダー固有のデータ・ソース属性を介して設定されます。
SQLJ/明示的なキャッシングとJDBCの暗黙的なキャッシングの機能には、異なるセマンティクスと動作があります。前述のように、SQLJ文キャッシングは、ループや同じメソッドへ繰り返して実行されるコールなど、繰り返し使用される単一の文にのみ適用されます。次の例を検討してください:
...
#sql { same SQL operaton }; // occurrence #1
...
Java code
...
#sql { same SQL operaton }; // occurrence #2
...
Java code
...
#sql { same SQL operaton }; // occurrence #3
...
3つのSQL操作は、空白も含めてすべて同じであると想定します。
SQLJキャッシングでは、これら3つの同じSQL操作は、それぞれ異なる文であると考えられています。それらは、キャッシュ内で3つの別々のスロットを占有します。ただし、JDBCの暗黙的なキャッシングでは、3つすべてに1つのキャッシュ・スロットを使用するので、これらを同じ文として認識します。文は、2番目と3番目の文で再利用されます。
文キャッシングの制限とノート
文のキャッシュを使用すると、キャッシュ・サイズが1の場合でも、たいていのSQLJアプリケーションではパフォーマンスが向上します。ただしこの場合は、次の留意点があります。
-
各文が1回しか実行されない場合は、メリットがあるわけではありません。
-
複数回実行した文を使用して1回実行した文をインターリーブするのは避けてください。実行回数が1回のみである文を使用すると、文のキャッシュ中に不必要な領域がとられてしまうため、キャッシュ・サイズが上限を超える原因となります。ISOコード生成を使用している場合は、前述の方法のかわりに、実行回数が1回のみである文に対して個別の接続コンテキスト・クラスを使用して、この接続コンテキスト・クラスの文キャッシングを無効にできます。
-
SQL操作が同一でも別々の文は、別々の文と同様に扱われます。処理もキャッシングも別々に実行されます。別々の文を使用するかわりに、SQL操作を1つのメソッドに記述して、このメソッドを繰り返しコールする方法もあります。
-
文のキャッシュ・サイズは、慎重に選択してください。キャッシュ・サイズが小さすぎるとキャッシュが満杯になり、文が再実行されないうちにフラッシュしてしまいます。大きすぎる場合は、データベースやプログラムのリソースが使い果たされてしまう可能性があります。
文キャッシングに関する次の一般的なノートに従ってください。
-
Oracle固有コード生成の場合、別々のSQLJ接続コンテキスト・インスタンスを使用して、別の文キャッシングを動作させることは、接続コンテキストで同一の基になるJDBC接続インスタンスを共有していると、うまくいきません。これは、Oracle固有コード生成の場合、SQLJはJDBC文キャッシュを使用するためです。
-
Oracleアプリケーションでは、文のキャッシュ・サイズとアプリケーション(直接またはSQLJを使用)でオープンしているJDBC文の最大個数とを合計した数を、各セッションで利用できるカーソルの最大個数よりも少ない数とする必要があります。同時にオープンすることのできる文の最大個数は、カーソルの最大個数に基づいて決まるからです。
-
文のキャッシングを使用しても、通常は操作自体の実行セマンティクスは変わりませんが、場合によっては変わることもあります。たとえば、リソース解放時に例外をスローする文がある場合にキャッシュを使用すると、接続が終了するか、または文がキャッシュからフラッシュされる(キャッシュ・サイズが上限を超えるとき)まで、例外はスローされないことを意味します。
11.1.3 バッチ更新機能
バッチ更新機能(Sun MicrosystemsのJDBC 2.0の仕様でバッチ更新と呼ばれる)では、UPDATE、DELETEおよびINSERT文を使用可能です。これらの文は、バッチ可能で互換性があるため、1つのバッチにまとめて、一括してデータベースに転送されて実行されるので、データベースへのラウンドトリップ回数が減ります。この機能は、JDBCおよびSQLJ仕様に含まれており、Oracle JDBCおよびSQLJ実装でサポートされています。通常、バッチ更新機能は、ループ内で繰り返し実行される操作に有効です。
SQLJのバッチ更新機能は、実行コンテキストの使用とつながりがあります。このバッチ更新機能は、実行コンテキストごとに有効化/無効化の個別指定が可能で、各実行コンテキストのインスタンスにはそのインスタンス自体のバッチが保持されます。
ノート:
バッチ更新について、次の点に注意してください。
-
デフォルトのOracle固有コード生成を使用するか、ISOコード生成の場合はアプリケーションをOracleカスタマイザでカスタマイズする必要があります。
-
自動コミット・モードは使用禁止にすることをお薦めします。これによって、バッチ実行時にエラーが発生した場合に、コミットする対象とロールバックする対象を制御できるようになります。
バッチ可能かつ互換性のある文
ある文を既存のバッチの文に追加できるかどうかは、次のような2つの基準によって決まります。
-
バッチ可能かどうか。どんな場合にも、1つに一括できない文もあります。
-
既存のバッチの文との互換性がある文か。
SQLJの場合、次の種類の文をバッチで実行できます。
-
UPDATE -
INSERT -
DELETE
ただし、UPDATE文やINSERT文にストリーム・ホスト式を1つ以上記述した場合は、バッチでは実行できません。
SQLJでは、同じ文の複数のインスタンス間でのみ互換性が確保されます。このことは、次のどちらかの場合に起こります。
-
ループ内で文が繰り返し実行される場合
-
メソッドで文が実行され、しかもそのメソッドが繰り返しコールされる場合
バッチ更新の有効化および無効化
SQLJのバッチ更新機能は、実行コンテキスト・インスタンスごとに個別に実行されます。各実行コンテキスト・インスタンスは、他の実行コンテキスト・インスタンスとは独立してバッチ更新機能を有効化することが可能で、しかも各インスタンスは、そのインスタンス自体のバッチが保持されます。
特定の実行コンテキスト・インスタンスに対してバッチ更新機能の有効化/無効化を指定するには、その実行コンテキスト・インスタンスのsetBatching()メソッドを使用します。このメソッドは、入力としてブール値をとります。
... ExecutionContext ec = new ExecutionContext(); ec.setBatching(true); ...
または
... ExecutionContext ec = new ExecutionContext(); ec.setBatching(false); ...
バッチ更新機能は、デフォルトでは無効になります。
ノート:
既存の文のバッチは、setBatching()メソッドの処理対象にはなりません。バッチ更新の有効化/無効化には関係なく、既存のバッチについては実行や取消しが行われないようになっています。
ある実行コンテキストに対してバッチ更新機能が有効になっているかどうかを調べるには、その実行コンテキスト・インスタンスのisBatching()メソッドを使用します。
ExecutionContext ec = new ExecutionContext(); ... boolean batchingOn = ec.isBatching();
ただし、上に示した指定では、現在のバッチが未完了のままになっているかどうかは、提示されません。
明示的および暗黙的なバッチ実行
未完了のバッチ更新は、必要であれば明示的に実行できますが、状況によっては暗黙的に実行されます。
ノート:
バッチ実行中に例外が発生する場合もあるので、そうした例外による影響を考慮することは重要です。
バッチ更新機能を明示的に実行するには、実行コンテキスト・インスタンスのexecuteBatch()メソッドを使用します。このメソッドは、更新カウントのint型配列を戻り値としています。
バッチを明示的に実行する例を次に示します。
...
ExecutionContext ec = new ExecutionContext();
ec.setBatching(true);
...
double[] sals = ...;
String[] empnos = ...;
for (int i = 0; i < empnos.length; i++)
{
#sql [ec] { UPDATE employees SET salary = :(sals[i]) WHERE employee_id = :(empnos[i]) };
}
int[] updateCounts = ec.executeBatch();
...
ノート:
executeBatch()の起動時に、実行コンテキスト・インスタンスに未完了のバッチがなければ、このメソッドの戻り値はnullになります。
未完了のバッチが存在するときは、次のような場合に暗黙的に実行されます。
-
バッチ処理を行うことができない実行文が出現した場合。この場合最初に既存のバッチが実行の対象となり、次にバッチ処理の行うことができない文が実行されます。
-
あるUPDATE文がバッチ可能であっても、既存のバッチとは非互換(つまり、その文のインスタンスではない)。この場合は、バッチが実行された後で、非互換の文から新規のバッチが生成されます。
-
事前定義されたバッチ数の上限(指定された文の数)に達した場合。
次に例を示します。まず、あるバッチを作成され、バッチ可能でない文が出現したときに暗黙的に実行されます。次に、新しいバッチが作成され、バッチ可能でも互換性のない文が出現したときに暗黙的に実行されます。
ExecutionContext ec = new ExecutionContext();
ec.setBatching(true);
...
/* Statements in the following loop will be placed in a batch */
double[] sals = ...;
String[] empnos = ...;
for (int i = 0; i < empnos.length; i++)
{
#sql [ec] { UPDATE employees SET salary = :(sals[i]) WHERE employee_id = :(empnos[i]) };
}
/* a SELECT is unbatchable so causes the batch to be executed */
double avg;
#sql [ec] { SELECT avg(salary) INTO :avg FROM employees };
/* Statements in the following loop will be placed in a new batch */
double[] comms = ...;
for (int i = 0; i < empnos.length; i++)
{
#sql [ec] { UPDATE employees SET commission_pct = :(comms[i]) WHERE employee_id = :(empnos[i]) };
}
/* the following update is incompatible with the second batch, so causes it to be executed */
int smithdeptno = ...;
#sql [ec] { UPDATE employees SET department_no = :deptno WHERE first_name = 'Smith' };
暗黙的に実行されたバッチの更新カウントを取得するには、その実行コンテキスト・インスタンスのgetBatchUpdateCounts()メソッドを起動します。このメソッドの起動後、この実行コンテキスト・インスタンスで正常に実行されたバッチのうち、最後のバッチの実行更新カウントが戻り値として戻されます。SELECT文および最後のUPDATE文の後に、次のコード文が挿入される場合があります。
int[] updateCounts = ec.getBatchUpdateCounts();
ノート:
実行コンテキスト・インスタンスに対して正常に実行されたバッチ更新がなければ、getBatchUpdateCounts()の戻り値はnullになります。
バッチの取消し
実行コンテキストの未完了のバッチを取り消すには、その実行コンテキスト・インスタンスのcancel()メソッドを使用します。たとえば、バッチ実行時に発生した例外のイベント内で、実行済であるが、コミットされていないバッチを取り消すことができます。次に例を示します。
...
ExecutionContext ec = new ExecutionContext();
ec.setBatching(true);
...
double[] sals = ...;
String[] empnos = ...;
for (int i = 0; i < empnos.length; i++)
{
#sql [ec] { UPDATE employees SET salary = :(sals[i]) WHERE employee_id = :(empnos[i]) };
if (!check(sals[i], empnos[i])) //assume "check" is a user-supplied function
{
ec.cancel();
throw new SQLException("Process canceled.");
}
}
try
{
int[] updateCounts = ec.executeBatch();
} catch ( SQLException exception) { ec.cancel(); }
...
あるバッチを取り消した場合、次のバッチ可能な文から新規のバッチが開始されます。
ノート:
-
cancel()をコールすると、現時点で実行中の文もすべて取り消されます。 -
バッチを取り消しても、バッチ更新は無効になりません。
実行コンテキストの更新カウント
Oracle Database 12c リリース1 (12.1) SQLJ実装では、更新カウントの配列(実行コンテキスト・インスタンスのexecuteBatch()またはgetBatchUpdateCounts()メソッドの戻り値)には、バッチの文で更新された行数カウントは含まれていません。この戻り値で示されるのは、各文の更新が成功したかどうかです。このため、バッチ処理が無効である場合、実行コンテキスト・インスタンスのgetUpdateCount()メソッドの戻り値である単一の更新カウントと前に示したカウントは機能が異なるものです。いくつかの文を1つに一括してバッチ実行すると、getUpdateCount()からの戻り値である単一の更新カウントの内容も変更されます。
バッチ可能な環境では、実行コンテキスト・インスタンスのgetUpdateCount()メソッドの戻り値は、各文が出現するたびに修正されます。この戻り値は、ExecutionContextクラスのstatic int定数の値(次のうちの1つ)で更新されます。
-
NEW_BATCH_COUNT: 最後に出現した文に対して新しいバッチが生成されたことを示します。 -
ADD_BATCH_COUNT: 最後に出現した文が既存のバッチに追加されたことを示します。 -
EXEC_BATCH_COUNT: 最後の文が出現した後で、未完了のバッチが明示的または暗黙的に実行されたことを示します。
上に示した定数を参照するには、次の修飾名を使用します。
ExecutionContext.NEW_BATCH_COUNT ExecutionContext.ADD_BATCH_COUNT ExecutionContext.EXEC_BATCH_COUNT
バッチが明示的または暗黙的に実行された後にexecuteBatch()やgetBatchUpdateCounts()から戻された配列には、文の実行の正常・異常終了のみが示されます。各バッチの文には、それぞれ配列要素があります。JDBC 2.0仕様に従って、配列要素の値が-2であれば、対応する文の実行は正常終了したが、更新行数は不明であるという意味になります。
バッチの実行後に配列の値すべてに目を通しても、大して意味がありません。この戻り値としての配列に対するチェックとしては、実行前にバッチ形態にまとめられた文の数を確認することのみが挙げられ、具体的には実行の正常終了後(基本的には、例外を発生しないバッチ実行後)に配列の要素数をチェックします。
複数の文が一括されてそのバッチが実行される段階では、更新カウントの配列は未更新になっています。
バッチ制限の設定
事前定義された数の文がバッチされてから次の文が追加されるまでの間に、各更新バッチが実行されるように指定できます。次のように、実行コンテキスト・インスタンスのsetBatchLimit()メソッドを使用し、0(ゼロ)でない正の整数を入力します。
...
ExecutionContext ec = new ExecutionContext();
ec.setBatching(true);
ec.setBatchLimit(10);
...
double[] sals = ...;
String[] empnos = ...;
for (int i = 0; i < 20; i++)
{
#sql [ec] { UPDATE emp1 SET sal = :(sals[i]) WHERE empno = :(empnos[i]) };
}
このループの実行回数は20回ですが、文のバッチとバッチの実行は11回目のループ中、つまり11個目の文がバッチに追加される前に完了します。ループの中では、バッチはループの2回目には処理されないので注意してください。アプリケーションがループを抜けた後は、別の文を実行するか、バッチを明示的に実行するまでは最後の10個の文はこの未実行のバッチの中に残ります。
ExecutionContextクラスのstatic int定数(次の2つのどちらか)は、setBatchLimit()への入力として使用できます。
-
AUTO_BATCH: SQLJランタイムによるバッチ制限の判断を可能にします。 -
UNLIMITED_BATCH(デフォルト): バッチ制限がないことを示します。
次に例を示します。
... ExecutionContext ec = new ExecutionContext(); ec.setBatching(true); ec.setBatchLimit(ExecutionContext.AUTO_BATCH); ...
または
ec.setBatchLimit(ExecutionContext.UNLIMITED_BATCH); ...
現在のバッチ制限をチェックするには、実行コンテキスト・インスタンスのgetBatchLimit()メソッドを使用します。
非互換の文に対するバッチ処理
既存のバッチ形態の文とは互換性がない文をバッチ処理する際に、このバッチを暗黙的に実行しない場合は、別個の実行コンテキスト・インスタンスを使用してください。次に例を示します。
...
ExecutionContext ec1 = new ExecutionContext();
ec1.setBatching(true);
ExecutionContext ec2 = new ExecutionContext();
ec2.setBatching(true);
...
double[] sals = ...;
String[] empnos = ...;
for (int i = 0; i < empnos.length; i++)
{
#sql [ec1] { UPDATE emp1 SET sal = :(sals[i]) WHERE empno = :(empnos[i]) };
#sql [ec2] { UPDATE emp2 SET sal = :(sals[i]) WHERE empno = :(empnos[i]) };
}
int[] updateCounts1 = ec1.executeBatch();
int[] updateCounts2 = ec2.executeBatch();
...
ノート:
この例では、2つのUPDATE文が互いに依存しないことを前提としています。相互に依存しあう文を別々の実行コンテキストでバッチ処理するのは避けてください。これらの文が実行される順序を必ずしも保証できないためです。
かわりの方法としては、単一の実行コンテキストと別個のループを使用し、EMP1の更新をすべて一括して実行した後で、EMP2の更新を実行する方法があります。具体的には、次のようにします。
...
ExecutionContext ec = new ExecutionContext();
ec.setBatching(true);
...
double[] sals = ...;
String[] empnos = ...;
for (int i = 0; i < empnos.length; i++)
{
#sql [ec] { UPDATE emp1 SET sal = :(sals[i]) WHERE empno = :(empnos[i]) };
}
for (int i = 0; i < empnos.length; i++)
{
#sql [ec] { UPDATE emp2 SET sal = :(sals[i]) WHERE empno = :(empnos[i]) };
}
ec.executeBatch();
...
この例の最初のバッチは暗黙的に実行されているのに対し、2番目のバッチは明示的に実行されます。
暗黙的な実行コンテキストを使用したバッチ更新機能
ここまでのすべてのバッチ更新の例では、明示的な実行コンテキスト・インスタンスを指定してきました。あらゆる実行コンテキスト・インスタンスが暗黙的な実行コンテキスト・インスタントを持てば必要ありません。たとえば、デフォルト接続の暗黙的な実行コンテキスト・インスタンスを参照するには、次のようにします。
DefaultContext.getDefaultContext().getExecutionContext().setBatching(true);
...
double[] sals = ...;
String[] empnos = ...;
for (int i = 0; i < empnos.length; i++)
{
#sql { UPDATE employees SET salary = :(sals[i]) WHERE employee_id = :(empnos[i]) };
}
// implicitly execute the batch and commit
#sql { COMMIT };
または、次のようにこのバッチを明示的に実行することも可能です。
DefaultContext.getDefaultContext().getExecutionContext().executeBatch();
バッチ更新機能に関する一般的な注意
バッチ更新機能処理を使用する場合、特に、実行コンテキスト・インスタンスがバッチの文とバッチ形態でない文とを混合する場合は、次の点に注意してください。
-
バッチ形態でない文はバッチの文に依存するので、まずバッチの文を実行し、その後でバッチ形態でない文を実行する必要があります。
-
JDBC
COMMIT操作やROLLBACK操作、つまり、JDBCConnectionインスタンスを自動コミットするか、またはcommit()メソッドまたはrollback()メソッドを明示的に使用する場合は、バッチ中の未完了の文は実行されません。ただし、SQLJ
COMMITまたはROLLBACK文を次のように使用すると、バッチ内の未完了文が実行されます。#sql { COMMIT };または
#sql { ROLLBACK };この理由からも、変更内容のコミットやロールバックには、常に
#sql構文を使用することをお薦めします。この構文を使用すると、SQLJリソースとJDBCリソースの両方をクリーンアップできます。 -
バッチ処理できない文または互換性のない文が現れた結果としてバッチが暗黙的に実行される場合、そのバッチが実行されるのは、バッチ処理できない文または互換性のない文が実行される前で、文の入力パラメータが評価されて目的の文に渡された後です。
-
バッチ可能な実行コンテキスト・インスタンスのうち特定のインスタンスのみの使用を中止する際は、リソースを解放するために、未完了のバッチを暗黙的に実行するか、取り消すことをお薦めします。
バッチ実行中のエラー状態
ある文が原因でバッチ実行中に例外が発生した場合、次の点に注意してください。
-
例外発生の原因となった文より後のバッチの文は、実行されません。
-
例外発生前にすでに実行されたバッチの文は、ロールバックされません。
-
例外が発生したバッチが、別の(バッチ処理できないか非互換の)文が現れた結果として、暗黙的に実行された場合、その文は実行されません。
ノート:
バッチ更新の使用時には自動コミット・モードを無効にしてください。これによって、バッチ実行時にエラーが発生した場合、コミットおよびロールバックを制御できます。
JDBC 2.0以上でのバッチ実行中に例外が発生した場合、通常、それは標準java.sql.BatchUpdateExceptionクラスのインスタンス、java.sql.SQLExceptionクラスのサブクラスです。BatchUpdateExceptionクラスにはgetUpdateCounts()メソッドがあり、このメソッドは、例外発生前に正常に実行されたバッチ処理文に関して、ExecutionContextクラスのexecuteBatch()またはgetBatchUpdateCounts()メソッドから戻される内容と同等の更新カウントの配列を戻します。
再帰的コールインとバッチ更新機能
SQLJストアド・プロシージャでは、あるプロシージャから別のプロシージャをコールすると、2つのプロシージャが同時に1つの実行コンテキスト・インスタンスを使用することになります。バッチ更新フラグ(実行コンテキスト・インスタンスのsetBatching()メソッドを使用して設定)は、その他の実行コンテキストの属性と同じ動作をします。フラグを設定したストアド・プロシージャには関係なく、このフラグ設定値は、どちらかのストアド・プロシージャ内にある次の実行文に反映されます。
このため、再帰的コールインのたびにサーバーではバッチ更新機能が自動的に無効化されます。再帰的に起動されるプロシージャでは、未完了のバッチは実行されますが、バッチ処理は行われなくなります。こうした動作を回避するには、バッチ可能なストアド・プロシージャで明示的な実行コンテキスト・インスタンスを使用します。
11.1.4 列の定義
Oracle SQLJ実装では、列の型およびサイズの定義にOracle JDBCのサポートが反映されています。ドライバの実装は、Oracle JDBCドライバごとに若干異なりますが、列の型およびサイズを登録すると、問合せごとのデータベースとのやり取りが軽減されます。このことは特に、Oracle JDBC Thinドライバと位置イテレータの使用に当てはまります。
列の定義に関するOracle実装
Oracle SQLJ実装では列の定義を有効にすると、次に示したような、列型とサイズを登録するためのステップが自動的に開始されます。
-
デフォルトのOracle固有コード生成を使用したカスタマイズ時または変換時に、SQLJは、特定のデータベース・スキーマに接続して、取得する列の型およびサイズを確認します。ISO標準SQLJコード生成の場合、列のデフォルトはSQLJプロファイルの一部になります。この処理が行われるのは、ソース・コードの変換をカスタマイズするためのステップの際または既存プロファイルを個別にカスタマイズする際です。
-
アプリケーション稼働時、SQLJランタイムでは列情報を利用し、JDBCドライバで列型とサイズを登録します。この際にOracle JDBC文のクラスの
defineColumnType()メソッドへのコールが使用されます。
列の定義に使用するカスタマイザおよびトランスレータのオプション
列の定義を有効化するには、SQLJのオプションを次のように設定します。
-
optcolsフラグを有効にします。Oracle固有コード生成の場合は、SQLJトランスレータの-optcolsオプションを使用します。ISO標準コード生成の場合は、トランスレータのオプションまたはOracleカスタマイザのオプションを使用します(SQLJコマンドラインで-P-Coptcolsと入力します)。 -
データベース接続用のユーザー、パスワードおよびURLを設定します。Oracle固有コード生成の場合、この設定はSQLJトランスレータの
-user、-passwordおよび-urlの各オプションを介して行います。ISO標準コード生成の場合、この設定は、トランスレータのオプションを介して、またはカスタマイザのオプションを個別に使用して行われます(SQLJコマンドラインで-P-user、-P-passwordおよび-P-urlを入力します)。デフォルトのOracleDriverクラスを使用する場合は、さらに、JDBCドライバ・クラスも設定します(SQLJコマンドラインで-P-driverと入力します)。
カスタマイザのオプションの詳細は、「カスタマイザ固有のオプションの概要」のoptcolsの項、および「カスタマイザ・ハーネスのオプションの概要」のuser、password、urlおよびdriverの項を参照してください。
11.1.5 パラメータ・サイズの定義
Oracle JDBCおよびSQLJ実装では、JDBCリソース割当て量を最適化できます。このためには、次のいずれかに使用するパラメータ・サイズ(Javaホスト変数のサイズ)を定義します。
-
ストアド・プロシージャまたはファンクション・コールの入出力パラメータ
-
ストアド・ファンクション・コールからの戻り値
-
SET文の入出力パラメータ
-
PL/SQLブロックの入出力パラメータ
パラメータ・サイズの定義に関するOracle実装
Oracleでは、オプション設定値とソース・コードのコメントに埋込みのヒントとの組合せによって、パラメータ・サイズが実装されます。ISO標準SQLJコード生成の場合、Oracleカスタマイザのオプションが使用できます。デフォルトのOracle固有コード生成の場合、同等のSQLJトランスレータのオプションが使用できます。
次のように、オプションおよびヒントを使用します。
-
SQLJトランスレータまたはOracleカスタマイザのパラメータ定義フラグを使用して、パラメータ・サイズの定義を有効にします。
-
SQLJトランスレータまたはOracleカスタマイザのパラメータ・デフォルト・サイズ・オプションを使用して、特定のデータ型のデフォルト・サイズを指定します。
-
ソース・コードのコメントにヒントを次の書式で埋め込んで、特定のデータ型のデフォルト・サイズを指定変更します。
指定したホスト変数に対するパラメータ・サイズの定義を使用可能にすると、ソース・コード・ヒントがあればそれに従ってリソースが割り当てられます。ソース・コード・ヒントがない場合、対応するデータ型のデフォルト・サイズが指定されていれば、そのデフォルト・サイズが使用されます。ソース・コード・ヒントも該当のデフォルト・サイズも指定されてない場合、JDBC実装に基づいて最大のリソース量が割り当てられます。
アプリケーション実行時に、Oracle JDBC文のクラスで使用できるdefineParameterType()メソッドとregisterOutParameter()メソッドをコールすることによって、パラメータ・サイズが登録されます。
ノート:
パラメータ定義フラグを有効にしなかった場合は、パラメータ・サイズのデフォルトとソース・コード・ヒントが無視され、JDBC実装に基づいて最大またはデフォルトのリソースが割り当てられます。
パラメータ・サイズの定義に使用するカスタマイザとトランスレータのオプション
パラメータ・サイズの定義には、次のSQLJのオプションを使用します。
-
パラメータ・サイズの定義を有効化するには、
optparamsフラグを使用します。Oracle固有コード生成の場合は、SQLJトランスレータの-optparamsオプションを使用します。ISO標準コード生成の場合は、トランスレータのオプションまたはOracleカスタマイザのオプションを使用します(SQLJコマンドラインで-P-Coptparamsと入力します)。 -
特定のデータ型にデフォルトのサイズを設定するには、
optparamdefaultsを使用します。Oracle固有コード生成の場合は、SQLJトランスレータの-optparamdefaults=xxxxオプションを使用します。ISO標準コード生成の場合は、トランスレータのオプションまたはOracleカスタマイザのオプションを使用します。SQLJコマンドラインで-P-Coptparamdefaults=xxxxと入力します。
パラメータ・サイズの定義に使用するソース・コード・ヒント
パラメータ・サイズ定義のためのヒントは、次の形式でSQLJ文のソース・コードに埋め込みます(必要であれば、コメント内に空白文字を追加してもかまいません)。
/*(size)*/
サイズはバイト単位になっています。optparamsオプションを無効にすると、ヒントが無視されます。
デフォルトのパラメータ・サイズは、新規のサイズを指定せずに(JDBC実装でのサイズの自動割当てにより)指定変更するには、次のようにします。
/*()*/
次はその例です。
byte[] hash;
String name=Tyrone;
String street=2020 Meryl Street;
String city=Wichita;
String state=Kansas;
String zipcode=77777;
#sql hash = { /* (5) */ VALUES (ADDR_HASH(:name /* (20) */, :street /* () */,
:city, :state, :INOUT zipcode /* (10) */ )) };
結果式のヒント(前述の例では結果式hash)は、SQLJ文の大カッコの内側に記述します。入出力ホスト変数のヒントは、前述の例に示したように、このホスト変数のすぐ後ろに記述する必要があります。
この例では、パラメータ・サイズが次のように設定されます。
-
hash: 5バイト -
name: 20バイト -
street: デフォルトを設定なしで指定変更します(JDBCによる自動割当て) -
city: なし(該当のデータ型のデフォルトが指定されていればそれが使用されます) -
state: なし(該当のデータ型のデフォルトが指定されていればそれが使用されます) -
zipcode: 10バイト
ノート:
いずれかのパラメータ・サイズが変更された場合(実行時に実際のサイズが登録済のサイズを超えた場合など)、SQLの例外がスローされます。
11.2 SQLJのデバッグ機能
ここでは、Oracle SQLJ実装のデバッグ機能をまとめます。内容は次のとおりです。
11.2.1 デバッグ用のSQLJ -linemapフラグ
-linemapフラグを有効にすると、SQLJソース・コード・ファイルの行番号が、対応する.classファイルの場所にマッピングされます。このファイルは、SQLJトランスレータで生成された.javaファイルのコンパイルで生成される.classファイルになります。この結果、Javaランタイム・エラーが発生した場合、Java Virtual Machine(JVM)で報告された行番号がSQLJソース・コードでの行番号となり、デバッグがはるかに容易になります。
Sun社jdbデバッガを使用する際は、-linemapオプションのかわりに、-jdblinemapオプションを使用します。これらのオプションの機能は同等ですが、-jdblinemapではいくつか特別な処理を実行する点が異なります。この特別な処理とは、jdbではファイル拡張子が.javaであるJavaソース・ファイルのみがサポートされるために必要となる処理です。
ノート:
サーバー側で変換すると、その変換で生成されたクラス・スキーマ・オブジェクトが、SQLJソース・コードにマッピングしてある行番号を自動的に参照します。クライアント側での変換時に-linemapオプションを有効化すると、サーバー側と同様の自動参照が行われます。
11.2.2 AuditorInstaller専用カスタマイザの概要
ISOコード生成の場合、SQLJでは、AuditorInstallerという特別なカスタマイザが提供されます。このカスタマイザは、SQLJコマンドラインで指定したプロファイルに、オーディタと呼ばれるデバッグ文のセットを挿入します。これらのプロファイルは、前回のカスタマイズによって生成済であることが必要です。アプリケーションを実行すると、SQLJランタイムでデバッグ文が実行され、メソッド・コールと戻り値のトレース情報が表示されます。
デバッグ文を挿入するには、カスタマイズの一般オプションの場合と同じように、先頭に-P-を付けて、カスタマイザ・ハーネスのdebugオプションを使用します。
11.3 Oracle Performance MonitoringのSQLJサポート
将来(10i/10.1より後): ローカルに階層として格納されるSQLJ DMS監視、-sqlmonitor.dms=false設定(DMSに送信するかわりにローカルに格納するため)、SQLJ DMS APIの使用(ローカルに格納されている結果にアクセスするため)。
将来(10i/10.1より後): SQLJ -components=append、DMS用の複数の変換実行。
将来: 適宜、Oracle9iASをOracle10iASに更新してください。
ここでは、Oracle Dynamic Monitoring Service(DMS)に対するOracle SQLJ実装のサポートについて説明します。
11.3.1 SQLJ DMSサポートの概要
DMSを使用すると、ユーザーは、SQLJプログラムのパフォーマンス統計を測定できます。DMSのSQLJサポートは、実行時間などの各SQL文の全体的なパフォーマンスに焦点を当てていますが、DMSのOracle JDBCサポートのように、メソッドレベルまたはクラスレベルのパフォーマンス情報を提供することもできます。クライアント側の観点(各#sql文の全体的なパフォーマンスなど)か、またはサーバー側の観点(各SQL操作のサーバー側トレースなど)か、あるいはその両方を選択できます。
プログラムのインストルメントは、DMS設定を可能にするために必要であり、SQLJオプションを介して変換時に指定されます。インストルメントとは、具体的にはパフォーマンスを測定するためにDMSコールをシステムまたはアプリケーション・コードに挿入するプロセスです。
実行時に、変換時にインストルメントされたすべてのコンポーネントを、SQLJ DMSプロパティ・ファイル内の指示に従って監視できます。実行時、統計がDMS APIを介してDMSに送信されます。これには、環境でDMSシステムが実行されている必要があります。統計には、DMSツールを介してアクセスできます。
統計は、SQL文のパフォーマンスを追跡および理解するために役立ちます。また、統計は次の階層に従って(上から下に)レポートされます。
-
アプリケーション: このコンテキストでは、アプリケーションは、変換のためにSQLJコマンドラインで指定されたSQLJコンポーネントとJavaコンポーネントの構成と定義されます。ただし、SQLJコンポーネントのみがインストルメント可能です。
-
モジュール: モジュールはJavaパッケージに対応します。
-
アクション: アクションは、SQLJプログラムに定義されたJavaクラスにマップします。
-
文: 文は、SQLJプログラム内のSQL文です。
次のDMS統計は、クライアント側の監視のために測定されます。
-
各
#sql文の経過時間(解析および実行を含む) -
各
next()コールの実行時間であるGet-next時間 -
各
getXXX()コールを介したデータベース列の抽出時間であるGet-XXX時間
これらの統計では、変換時と実行時の両方でDMSライブラリがCLASSPATH内に存在する必要があります。したがって、サーバーでは、DMSライブラリを使用できないため、これらの統計はサポートされません。サーバー側のSQLJコードは、クライアント側のコードと同じ方法では監視できません。
次の統計は、SQLJクライアント・プログラムのサーバー側のSQL監視のために測定されます。
-
解析時間
-
実行時間
-
フェッチ時間
これらの統計は、SQLトレース機能を介して、Oracle Database 12c リリース1 (12.1)のトレース・ファイルから取得できます。これはDMSとは関係ありませんが、SQLJ DMSプロパティ・ファイルsqlmonitor.servertracingの設定を介して使用可能にできます。
クライアント側のSQLJプログラムには、DMS統計とサーバー側トレースの両方を使用できます。たとえば、DMSから1つの問合せで構成される#sql文に必要な合計時間を取得でき、次にサーバー側トレースから、サーバー内でのSQL問合せの実行にかかった実際の時間を取得できます。
ノート:
-
現在、DMSサポートにはOracle固有コード生成が必要です。これは、デフォルトで有効になっています。
-
Oracle Database 12c リリース1 (12.1)では、インストルメント済のコードにJava Development Kit (JDK) 6が必要です。
-
SQLJ宣言と文のみがインストルメントされます。
-
DMSライブラリは、Oracle Database 12c リリース1 (12.1)の
ORACLE_HOME/oc4j/libにあるファイルdms.jarにあります
11.3.2 DMS用のSQLJコマンドライン・オプションの概要
将来(10i/10.1より後): -components=append、複数の変換実行。
Oracle SQLJ実装では、DMSをサポートするための次のトランスレータ・フロントエンド・オプションが提供されます。
-
-instrument: インストルメントを有効にし、アプリケーション(変換するコンポーネントの集合)の名前を指定します。 -
-components: インストルメントするコンポーネント(パッケージおよびクラス)を指定します。
通常、-instrument設定で目的のアプリケーション名を指定し、必要に応じてパッケージも指定して、インストルメントを有効にします。または、trueを指定して、デフォルトのアプリケーションdefaultAppを使用します。DMSのインストルメントで、アプリケーションという用語は、SQLJコマンドラインで変換のために指定されたすべてのSQLJコンポーネントとJavaコンポーネントを指します。
インストルメントが有効な場合、SQLJ DMSプロパティ・ファイルは、-instrumentの設定(カレント・ディレクトリを基準とする)およびSQLJ -dオプションのすべての設定に従って作成されます。trueを設定すると、カレント・ディレクトリ内のプロパティ・ファイルの名前がsqlmonitor.propertiesになります。
ノート:
-instrumentの設定は、-instrument=trueを設定することと同じです。-instrument=false(デフォルト)を設定すると、インストルメントは無効になります。
簡単な例として、-instrument=myappを設定すると、プロパティ・ファイルmyapp.propertiesが作成されます。ここで、アプリケーション名がstock、パッケージ名がcom.acmeの次の例を考えます。
% sqlj -instrument=com.acme/stock -d /home Stock.sqlj Trading.sqlj
-dオプションが指定されているため、/home/com/acme/stock.propertiesファイルが作成されます。
-instrumentオプションでインストルメントを有効にしている場合は、-componentsオプションを使用して、DMS監視のためにインストルメントする変換済コンポーネントのサブセットを指定します。通常は、実行時の監視に柔軟性を持たせるために、ほとんど、またはすべてのコンポーネントを指定します。(各パッケージ内のすべてのクラスをインストルメントするために)パッケージのカンマ区切りリストを指定するか、特定のクラスを指定するか、またはデフォルトのall設定を使用して変換するすべてのコンポーネントをインストルメントします。
たとえば、クラスStockおよびTradingをインストルメントするには、次の構文を使用します。
% sqlj ... -components=com.acme.Stock,com.acme.Trading
実行時に、インストルメントされたコンポーネントが、SQLJ DMSプロパティ・ファイル内の指定に従って監視されます。変換時にインストルメントされていないコンポーネントは、プロパティ・ファイル内の指定にかかわらず、いずれも実行時に監視できません。
11.3.3 DMS用のSQLJランタイム・コマンドおよびプロパティ・ファイル設定
SQLJトランスレータで監視機能のためにファイルをインストルメントするかどうかはSQLJの-instrumentオプションで指定しますが、実行時に何をどのように監視するかを実際に決定するのはSQLJ DMSプロパティ・ファイルです。
このプロパティ・ファイルは変換時にSQLJによって作成され、必要に応じて変更できます。SQLJを再実行する場合、SQLJによってプロパティ・ファイルが上書きされることに注意してください。ファイルに行ったすべての変更は失われます。
SQLJプロパティ・ファイルでは、次の設定を行います。
-
sqlmonitor.components: インストルメントされたコンポーネント(パッケージまたはクラス)のカンマ区切りリストです。これは、SQLJの-componentsオプションの設定を反映して、トランスレータによって自動的に設定されます。 -
sqlmonitor.monitorcomp: 監視するコンポーネント(パッケージまたはクラス)のカンマ区切りリストであり、sqlmonitor.components設定内のコンポーネントのサブセットを示します。sqlmonitor.monitorcompは、sqlmonitor.components設定を反映して変換時に初期設定されますが、必要に応じて調整できます。allを設定すると、sqlmonitor.components設定にリストされたすべてのコンポーネントが監視されます。 -
sqlmonitor.dms: このブール・フラグにデフォルト値のtrueを設定して、収集した統計をDMSに送信することを指定します。これには、DMSツールを使用可能なOracle Application Server 10g インスタンスが実行されている必要があります。統計は、Webブラウザで表示するか、またはファイルに書き込むことができます。ノート:
現在、
sqlmonitor.dms=falseの設定はサポートされていません。将来(10i/10.1より後): sqlmonitor.dms=falseのサポート。(sqlmonitor.dms=falseの場合、DMSライブラリは引き続き必要か。)
-
sqlmonitor.sysurl: サーバー側トレースに対して、データベースURLを指定します。 -
sqlmonitor.sysuser: サーバー側トレースに対して、データベース・ユーザーを指定します。このユーザーには、sysdba権限が必要です。 -
sqlmonitor.syspassword: サーバー側トレースに対して、sysuserのパスワードを指定します。ノート:
sysurl、sysuserおよびsyspasswordのデフォルト値は、SQLJコマンドラインまたはSQLJプロパティ・ファイルのいずれかを介してSQLJに指定されたuser、passwordおよびurlの値に従います。 -
sqlmonitor.servertracing: サーバー側トレースを有効にして、SQL操作に関する統計などのサーバー内のパフォーマンス統計を収集するかどうかを指定します。trueまたはfalse(デフォルト)の設定がサポートされています。 -
sqlmonitor.dumpfile: DMSに統計を送信する場合、このオプションを使用して、DMSツールで統計を書き込むファイルを指定できます。デフォルトはapplication_name.mtrであり、application_nameは、-instrumentオプションの設定に従うか、またはデフォルトのdefaultAppになります。
11.3.4 SQLJ DMSセンサーとメトリック
センサーは、インストルメントされたSQLJプログラムの実行中にDMSでパフォーマンス・メトリックを計算するために使用され、DMSに送信されます。センサーは階層で編成され、各センサーにはパス名が付きます。一般的なセンサーの形式を次に示します。
/SQLJ/application_name/sensor_name /SQLJ/application_name/module/sensor_name /SQLJ/application_name/module/class/sensor_name /SQLJ/application_name/module/class/linenum/sensor_name
センサーはoracle.dms.instrument.Sensorクラスのインスタンスで、このクラスには、パフォーマンス統計を計算および編成するためのメソッドが含まれます。たとえば、追加のメトリックを導出し、いずれかのメトリックの値を取得するようにセンサーに指示するメソッドが存在します。
インストルメントしたアプリケーションの終了の前に、oracle.sqlj.runtime.sqlmonitor.SQLMonitorクラスのclose()メソッドへのコールが必要なことに注意してください。次に例を示します(接続コンテキストを終了するためのOracleクラスのclose()メソッドも使用されています)。
try
{
Oracle.close();
oracle.sqlj.runtime.sqlmonitor.SQLMonitor.close();
}
catch( Throwable e ) { ... }
次の用語に注意してください。
-
application_nameはアプリケーションの名前であり、SQLJ-instrumentオプションの設定に従うか、またはデフォルトのdefaultAppになります。 -
センサーがパッケージに関連付けられている場合、
moduleはパッケージ名になります。パッケージ名を省略した場合、設定*TopLevel*が使用されます。 -
センサーがクラスに関連付けられている場合、
classはクラス名になります。 -
センサーがSQL文に関連付けられている場合、
linenumはインストルメントされているSQLJプログラム内のSQL文の行番号を示します。複数のSQL文が同じ行に存在する場合、開始列の位置を使用して文が区別されます。たとえば、linenumの値が8.13の場合、8は行番号、13は列番号を示します。
次のセンサーと関連付けられたメトリックは通常、特定の用途に使用されます。
-
センサー名:
ContextType/SQLJ/application_name/module/class/linenum/ContextType
メトリック:
-
value: 接続コンテキスト型を示す文字列
-
-
センサー名:
SQLString/SQLJ/application_name/module/class/linenum/SQLString
メトリック:
-
value: SQL文で構成される文字列これはJDBCに渡される完全な文字列であり、元の
#sql文から行われたすべての変換を含みます。
-
-
センサー名:
Execute/SQLJ/application_name/module/class/linenum/Execute
メトリック:
-
time: この文に対するJDBCexecute()メソッドのすべての実行の合計時間(ミリ秒)たとえば、文が5回実行された場合、
timeは5回の実行に対してexecute()メソッドの実行にかかった合計時間になります。 -
completed: 完了した実行の数(5など) -
minTime: 任意の1回の実行の最小時間 -
maxTime: 任意の1回の実行の最大時間 -
avg:timeをcompletedで割った平均実行時間 -
active: プログラムの実行の終わりに文を実行しているスレッドの数(通常は0) -
maxActive: プログラムの実行中に文を実行したスレッドの最大数
JDBC文の実行時間を測定するために、クロックが文の実行直前に開始され、結果セットが取得されるか、文が実行を終了するか、または例外が捕捉されたときに停止します。
-
-
センサー名:
ServerExecute/SQLJ/application_name/module/class/linenum/ServerExecute
メトリック:
-
value: このSQL文のすべての実行に対するサーバーでの合計実行時間(ミリ秒) -
count: 完了した実行の数 -
minValue: 任意の1回の実行の最小時間 -
maxValue: 任意の1回の実行の最大時間
-
-
センサー名:
ServerFetch/SQLJ/application_name/module/class/linenum/ServerFetch
メトリック:
-
value: このSQL文のすべての実行に対するサーバーでの合計フェッチ時間(ミリ秒) -
count: 完了した実行の数 -
minValue: 任意の1回の実行の最小時間 -
maxValue: 任意の1回の実行の最大時間
-
-
センサー名:
ServerParse/SQLJ/application_name/module/class/linenum/ServerParse
メトリック:
-
value: このSQL文のすべての実行に対するサーバーでのSQL文の合計解析時間(ミリ秒) -
count: 完了した実行の数 -
minValue: 任意の1回の実行の最小時間 -
maxValue: 任意の1回の実行の最大時間
-
-
センサー名:
Next/SQLJ/application_name/module/class/linenum/Next
メトリック:
-
time: このSQL文のすべての実行に対する結果セット・イテレータのnext()メソッドの合計実行時間(ミリ秒) -
completed: 完了した実行の数(5など) -
minTime: 任意の1回の実行の最小時間 -
maxTime: 任意の1回の実行の最大時間 -
avg:timeをcountで割った平均実行時間 -
active: プログラムの実行の終わりに文を実行しているスレッドの数(通常は0) -
maxActive: プログラムの実行中に文を実行したスレッドの最大数
-
11.3.5 SQLJ DMSの例
SQLJプログラムExprDemo.sqljをインストルメントするコマンドラインの例(全体を1行に入力)を次に示します。
% sqlj -dir=. -instrument=a.b.c/app -components=all
-user=HR -url=jdbc:oracle:oci:@ ExprDemo.sqlj
Password: password
ノート:
dms.jarがCLASSPATH内に存在することを確認してください。
このコマンドを実行すると、次のファイルが生成されます。
-
./a/b/c/ExprDemo.java(-dirオプションの設定およびExprDemo.sqljでのパッケージa.b.cの宣言の結果) -
./a/b/c/app.properties(-instrumentオプションの設定の結果)
SQLJ DMSプロパティ・ファイルの例
app.propertiesの内容の例を次に示します。この例では、SQLJによって作成された後にファイルを編集して、いくつかの設定をデフォルト以外に変更しているとします。
sqlmonitor.components=all sqlmonitor.monitorcomp=all sqlmonitor.dms=true sqlmonitor.servertracing=true sqlmonitor.sysurl=jdbc:oracle:oci:@ sqlmonitor.sysuser=HR sqlmonitor.syspassword=hr sqlmonitor.dumpfile=a/b/c/app.mtr
ノート:
SQLJトランスレータを再実行した場合、app.propertiesが上書きされ、加えたすべての変更が失われます。
統計の例
sqlmonitor.dms=trueを設定すると、監視する統計がDMSに送信されます。sqlmonitor.dumpfile値が設定されている場合、プログラムのコンパイルおよび実行時に、DMSツールによって統計が./a/b/c/app.mtrファイルに書き込まれます。
特定のコード例に対する統計を検査するExprDemo.sqljの部分を次に示します。
#sql
{
DECLARE
n NUMBER;
s NUMBER;
BEGIN
n := 0;
s := 0;
WHILE n < 100 LOOP
n := n + 1;
s := s + :IN (indx++);
END LOOP;
:OUT total := s;
END;
};
前述のコード例に関連し、実行時間およびサーバー実行時間を表示するapp.mtr内の統計の部分を次に示します。
SQLString.value: DECLARE n NUMBER; s NUMBER;
BEGIN n := 0; s := 0; WHILE n < 100 LOOP
n := n + 1;
s := s + :1 ;
END LOOP; :2 := s; END; statement SQL string
ServerExecute.maxValue: 20.0 server_execute_time
ServerExecute.minValue: 20.0 server_execute_time
ServerExecute.count: 0 ops
ServerExecute.value: 20.0 server execute time
ServerFetch.maxValue: 0.0 server_fetch_time
ServerFetch.minValue: 0.0 server_fetch_time
ServerFetch.count: 0 ops
ServerFetch.value: 0.0 server fetch time
ServerParse.maxValue: 0.0 server_parse_time
ServerParse.minValue: 0.0 server_parse_time
ServerParse.count: 0 ops
ServerParse.value: 0.0 server parse time
193.5
ContextType.value: class sqlj.runtime.ref.DefaultContext
statement connection context
Execute.maxActive: 1 threads
Execute.active: 0 threads
Execute.avg: 37.0 msecs
Execute.maxTime: 37 msecs
Execute.minTime: 37 msecs
Execute.completed: 1 ops
Execute.time: 37 msecs
これらの統計は、JDBCクライアントでの(1回の実行の)合計実行時間が37ミリ秒であり、サーバーでの実行時間が20ミリ秒であることを示します。
イテレータの統計の例
ExprDemo.sqljでは、次のようにイテレータ型Iterの定義および実行も行われます。
#sql public static iterator Iter(String ename);
....
Iter iter;
#sql iter = { select first_name from employees};
while (iter.next())
{
System.out.println(iter.ename());
}
DMSでは、イテレータに対するnext()操作の実行時間が収集されます。イテレータ型Iterに対するDMSの結果の例を次に示します。
Iter
Next.time: 5 msecs
この結果は、Iterインスタンス内を反復中にnext()操作にかかった合計時間が5ミリ秒であったことを示しています。
接続コンテキストの統計の例
ExprDemo.sqlj内の#sql文では、デフォルトの接続コンテキストが使用されます。プログラム全体で使用されるDefaultContextインスタンスに対して、DMSは次の統計を戻します。
class_sqlj.runtime.ref.DefaultContext
StmtCacheSize.value: 5 statement cache size
StmtsExecuted.count: 7 ops
StmtsCacheExecuted.count: 7 ops
この結果は、コンテキストに5つの文の文キャッシュ・サイズが含まれることを示しています。合わせて、7つのSQL文が実行されています。