21 パフォーマンス拡張機能
この章では、Java Database Connectivity(JDBC)標準に対するOracleパフォーマンス拡張機能について説明します。
この章の構成は、次のとおりです。
ノート:
Oracleバッチ更新は、Oracle Database 12c リリース1 (12.1)で非推奨となりました。Oracle Database 12cリリース2 (12.2)以降、Oracleバッチ更新はオペレーション・コードなし(no-op)になりました。つまり、Oracle Database 12cリリース2 (12.2)のJDBCドライバを使用してアプリケーションでOracleバッチ更新を実装すると、指定したバッチ・サイズが設定されず、バッチ・サイズが1になるということです。バッチがこの設定の場合、アプリケーションは一度に1行ずつを処理します。Oracle Database 12cリリース2 (12.2)のJDBCドライバを使用する場合は、標準のJDBCバッチを使用することを強くお薦めします。
21.1 バッチ更新
この項の内容は次のとおりです。
21.1.1 バッチ更新の概要
複数のUPDATE
文、DELETE
文またはINSERT
文を単一のバッチにグループ化して、バッチ全体を一度にデータベースに送信して処理することによって、データベースへのラウンドトリップの回数を減らし、それによってアプリケーションのパフォーマンスを向上させることができます。これは、バッチ更新(update batching)と呼ばれます。これは、特にプリペアド文で、同じ文をバインド変数を変えて繰り返し使用する場合に効果的です。
ノート:
-
JDBC 2.0仕様では、これをバッチ更新(batch updates)と呼びます。
-
JDBC 2.0標準に準拠するために、標準バッチ更新のOracle実装では、プリペアド文と同じように、
OUT
パラメータなしのコール可能文および一般的な文もサポートされています。標準バッチ更新は、Oracle JDBCアプリケーションに簡単に移行できます。ただし、標準バッチ更新のOracle実装では、一般的な文およびコール可能文の実際のバッチ処理は実装されていないため、パフォーマンスの向上が確認されるのは、PreparedStatement
オブジェクトの場合のみです。
21.1.2 標準バッチ更新
JDBC標準バッチ更新は、addBatch
メソッドを使用して明示的に文をバッチに追加し、executeBatch
メソッドを使用して明示的にバッチを処理します。
ノート:
バッチ更新を使用する場合、自動コミット・モードは無効化します。バッチの処理中にエラーが発生した場合、エラーの前に正常に実行された操作をコミットするかロールバックするかを選択できます。
21.1.2.1 標準バッチ処理のOracle実装の制限事項
この項では、標準バッチ更新のOracle実装に関する、制限事項と実装の詳細を説明します。
Oracle JDBCアプリケーションでバッチ更新を使用すると、バインド変数の設定を変えてプリペアド文を繰り返し処理できます。
標準バッチ更新のOracle実装では、一般的な文およびコール可能文の実際のバッチ処理は実装されていません。Oracle JDBCはStatement
およびCallableStatement
オブジェクトに対する標準バッチ処理の使用をサポートしますが、パフォーマンスは向上しません。
21.1.2.2 バッチに対する操作の追加について
いかなる文オブジェクトも、最初に作成されたとき、その文バッチは空です。文バッチに操作を追加するには、標準のaddBatch
メソッドを使用します。このメソッドは標準のjava.sql.Statement
インタフェース、PreparedStatement
インタフェースおよびCallableStatement
インタフェースで指定され、それぞれ、oracle.jdbc.OracleStatement
インタフェース、OraclePreparedStatement
インタフェースおよびOracleCallableStatement
インタフェースで実装されます。
Statement
オブジェクトの場合、addBatch
メソッドは、入力としてSQL操作のJava String
を取ります。次に例を示します。
... Statement stmt = conn.createStatement(); stmt.addBatch("INSERT INTO emp VALUES(1000, 'Joe Jones')"); stmt.addBatch("INSERT INTO dept VALUES(260, 'Sales')"); stmt.addBatch("INSERT INTO emp_dept VALUES(1000, 260)"); ...
この時点で、バッチには3つの操作が入っています。
プリペアド文の場合、バッチ更新は、異なる組合せのバインド・パラメータを使用して、同じ文を複数回実行するために使用されます。PreparedStatement
オブジェクトまたはOraclePreparedStatement
オブジェクトの場合、addBatch
メソッドは、入力を取りません。適切なset
XXX
メソッドによって最後に設定されたバインド・パラメータを使用して、操作をバッチに追加するだけです。CallableStatement
オブジェクトやOracleCallableStatement
オブジェクトの場合も同様です。ただし、標準更新バッチのOracle実装では、コール可能文をバッチ処理してもパフォーマンスはおそらく向上しないことに注意してください。
次に例を示します。
... PreparedStatement pstmt = conn.prepareStatement("INSERT INTO employees VALUES(?, ?)"); pstmt.setInt(1, 2000); pstmt.setString(2, "Milo Mumford"); pstmt.addBatch(); pstmt.setInt(1, 3000); pstmt.setString(2, "Sulu Simpson"); pstmt.addBatch(); ...
この時点で、バッチには2つの操作が入っています。
1つのバッチは単一のプリペアド文オブジェクトに関連付けられるので、バッチ処理できるのは、この例のような単一のプリペアド文の繰返し実行のみです。
21.1.2.3 バッチの処理について
現在の操作バッチを処理するには、文オブジェクトのexecuteBatch
メソッドを使用します。このメソッドは標準のStatement
インタフェースで指定されます。これは標準PreparedStatement
インタフェースとCallableStatement
インタフェースで拡張されています。
ノート:
addBatch
メソッドを数回コールして非常に多くの操作をバッチに追加して、非常に大きなバッチ(100,000行以上のバッチなど)を作成した場合、バッチでexecuteBatch
メソッドをコール中に、メモリーに関する深刻なパフォーマンスの問題が発生することがあります。このような問題を回避するために、JDBCドライバは透過的に大きなバッチを小さな内部バッチに分割し、各内部バッチに対してサーバーへのラウンドトリップを行います。各ラウンドトリップのオーバーヘッドのために、アプリケーションは若干遅くなりますが、メモリーはかなり最適化されます。ただし、各バインド行のサイズが非常に大きい場合(1MBを超える場合など)、このプロセスはパフォーマンス全体に悪影響を与えることがあります。これは、メモリーについて得られたパフォーマンスが時間について失われたパフォーマンスよりも少ないためです。
次の例では、前の例で示したプリペアド文のaddBatch
コールを繰り返した後、バッチを処理します。
... PreparedStatement pstmt = conn.prepareStatement("INSERT INTO employees VALUES(?, ?)"); pstmt.setInt(1, 2000); pstmt.setString(2, "Milo Mumford"); pstmt.addBatch(); pstmt.setInt(1, 3000); pstmt.setString(2, "Sulu Simpson"); pstmt.addBatch(); int[] updateCounts = pstmt.executeBatch(); ...
21.1.2.4 配列DMLの反復ごとの行数
Oracle Database 12cリリース1 (12.1)以降、executeBatch
メソッドは改良され、バッチのレコード数と同じサイズのint配列を戻します。戻り配列の各項目は、バッチの対応するレコードの影響を受けたデータベース表の行数です。たとえば、バッチ・サイズが5の場合、executeBatch
メソッドはサイズ5の配列を戻します。バッチの実行中にエラーが発生した場合、executeBatch
メソッドは値を戻すことができず、かわりにBatchUpdateException
をスローします。この場合、例外自体がサイズn
のint配列をそのデータとして保持します。n
は、成功したレコード実行の数です。たとえば、バッチのサイズが5で、4番目のレコードでエラーが発生した場合、BatchUpdateException
はサイズ3の配列(3レコードは正常に実行)を保持し、配列内の各項目は各レコードの影響を受けた行数を表します。
21.1.2.5 標準バッチ処理のOracle実装による変更のコミットについて
バッチを処理した後、変更をコミットする必要があります。自動コミットは使用禁止(推奨)にしてあるものとします。
commit
をコールすると、処理済の文バッチの場合、バッチ処理された操作とされなかった操作がコミットされますが、標準バッチのOracle実装では、未処理の保留文バッチには影響がありません。
21.1.2.6 バッチのクリアについて
現在の操作バッチを処理しないで消去するには、文オブジェクトのclearBatch
メソッドを使用します。このメソッドは標準のStatement
インタフェースで指定されます。これは標準PreparedStatement
インタフェースとCallableStatement
インタフェースで拡張されています。
次の点に注意してください。
-
バッチが処理されるとき、操作は、バッチに入れられた順に実行されます。
-
addBatch
をコールした後、executeUpdate
をコールする前に、executeBatch
またはclearBatch
をコールする必要があります。コールしないと、SQL例外が発生します。 -
clearBatch
またはexecuteBatch
をコールすると、文バッチは空にリセットされます。 -
接続が
ROLLBACK
要求を受信すると、文のバッチが空にリセットされません。リセットするには、clearBatch
を明示的にコールする必要があります。 -
ロールバック後の
clearBatch
メソッドのコールは、すべてのリリースで有効です。 -
文オブジェクトの現行結果セットがある場合、この結果セットは
executeBatch
コールによってクローズされます。 -
clearBatch
メソッドの戻り値はありません。
次の例では、前の例で示したプリペアド文のaddBatch
コールを繰り返した後、特定の条件でバッチをクリアします。
...
PreparedStatement pstmt =
conn.prepareStatement("INSERT INTO employees VALUES(?, ?)");
pstmt.setInt(1, 2000);
pstmt.setString(2, "Milo Mumford");
pstmt.addBatch();
pstmt.setInt(1, 3000);
pstmt.setString(2, "Sulu Simpson");
pstmt.addBatch();
if (...condition...)
{
int[] updateCounts = pstmt.executeBatch();
...
}
else
{
pstmt.clearBatch();
...
}
21.1.2.7 標準バッチ処理のOracle実装の更新件数
文バッチが正常に処理された場合、文のexecuteBatch
コールから戻される整数配列、つまり、更新件数配列には、常にバッチ操作1つに対して1つの要素が含まれます。標準バッチ更新のOracle実装では、配列要素の値は次のようになります。
-
プリコンパイル文のバッチの場合、配列には、各操作で影響を受けた行数を示す実際の更新件数を格納します。
-
一般的な文のバッチの場合、配列には、各操作で影響を受けた行数を示す実際の更新件数を格納します。標準バッチのOracle実装では、一般的な文の場合のみ、実際の更新件数がわかります。
-
コール可能文のバッチの場合、配列には、各操作で影響を受けた行数を示す実際の更新件数を格納します。
コードの側では、バッチの正常な処理に対して、配列要素に-2
、1
または実際の更新件数のいずれが設定されても処理できるように準備しておく必要があります。正常なバッチ処理では、配列にはすべて-2
が含まれるか、すべて1
が含まれるか、またはすべて正の整数が含まれます。
例21-1は、標準バッチ更新の使用方法を示しています。
例21-1 標準バッチ更新
この例は、前の項のサンプル・コードを組み合せたもので、次のステップを行います。
-
自動コミット・モードの無効化。どちらかのバッチ更新モデルを使用する場合、無効にする必要があります。
-
プリペアド文オブジェクトの作成。
-
プリペアド文オブジェクトに関連付けられたバッチへの操作の追加。
-
バッチの処理
-
バッチの操作のコミット。
conn.setAutoCommit(false); PreparedStatement pstmt = conn.prepareStatement("INSERT INTO employees VALUES(?, ?)"); pstmt.setInt(1, 2000); pstmt.setString(2, "Milo Mumford"); pstmt.addBatch(); pstmt.setInt(1, 3000); pstmt.setString(2, "Sulu Simpson"); pstmt.addBatch(); int[] updateCounts = pstmt.executeBatch(); conn.commit(); pstmt.close(); ...
更新件数配列を処理して、バッチが正常に処理されたかどうかを判断できます。
21.1.2.8 標準バッチ処理のOracle実装におけるエラー処理
executeBatch
がコールされたとき、バッチ処理された操作のうち1つでも失敗したり、結果セットを戻そうとしたりすると、処理は停止し、java.sql.BatchUpdateException
が生成されます。
バッチ例外の後、BatchUpdateException
オブジェクトのgetUpdateCounts
メソッドを使用して、更新件数配列を取り出せます。このメソッドからは、executeBatch
メソッドと同じように、更新件数のint
配列が戻されます。標準バッチ更新のOracle実装では、バッチが処理された後の更新件数配列の内容は次のようになります。
-
プリペアド文バッチにおいて、バッチの実行中にエラーが発生した場合、
executeBatch
メソッドは値を戻すことができず、かわりにBatchUpdateException
をスローします。この場合、例外自体がサイズn
のint配列をそのデータとして保持します。n
は、成功したレコード実行の数です。たとえば、バッチのサイズが5で、4番目のレコードでエラーが発生した場合、BatchUpdateException
はサイズ3の配列(3レコードは正常に実行)を保持し、配列内の各項目は各レコードの影響を受けた行数を表します。 -
一般的な文のバッチまたはコール可能文のバッチの場合、更新件数配列は、エラーの時点までの実際の更新件数を格納する、部分的な配列になります。標準バッチ更新のOracle実装では、Oracle JDBCは一般の文およびコール可能文の本当の意味でのバッチ処理を使用できないので、実際の更新件数がわかります。
たとえば、バッチに20の操作が含まれているとき、最初の13は正常終了し、14番目で例外が生成された場合、更新件数配列には13の要素が含まれ、正常終了した操作の実際の更新件数が設定されます。
この場合、正常終了した操作をコミットすることも、ロールバックすることもできます。
コードの側では、例外が発生した場合、バッチの失敗した処理に対して、配列要素に-3
または実際の更新件数のどちらが設定されても処理できるように準備しておく必要があります。失敗したバッチ処理では、すべてに-3
が含まれる完全な配列か、正の整数が含まれる部分配列が作成されます。
21.1.2.9 バッチ処理される文とバッチ処理されない文の混在について
文オブジェクトに操作の保留バッチがある場合、通常のバッチ処理されない操作の処理を行うためにexecuteUpdate
をコールできません。
ただし、文バッチに操作を追加する前か、バッチを処理した後で、バッチ処理されない操作を処理する場合は、バッチ処理される操作とバッチ処理されない操作を単一文オブジェクトに混在させることができます。つまり、文オブジェクトのexecuteUpdate
は、バッチ更新が空のときにのみコールできます。バッチが空でない場合、例外が生成されます。
たとえば、次のような順序は有効です。
... PreparedStatement pstmt = conn.prepareStatement("INSERT INTO employees VALUES(?, ?)"); pstmt.setInt(1, 2000); pstmt.setString(2, "Milo Mumford"); int scount = pstmt.executeUpdate(); // OK; no operations in pstmt batch pstmt.setInt(1, 3000); pstmt.setString(2, "Sulu Simpson"); pstmt.addBatch(); // Now start a batch pstmt.setInt(1, 4000); pstmt.setString(2, "Stan Leland"); pstmt.addBatch(); int[] bcounts = pstmt.executeBatch(); pstmt.setInt(1, 5000); pstmt.setString(2, "Amy Feiner"); int scount = pstmt.executeUpdate(); // OK; pstmt batch was executed ...
ある文オブジェクトのバッチ処理されない操作と、別の文オブジェクトのバッチ処理される操作を、コード上に混在させることはできます。異なる文オブジェクトは、バッチ更新操作に関して、互いに無関係です。COMMIT
要求は、バッチ処理されない操作すべてと、処理済バッチの正常な操作すべてに影響を与えますが、保留バッチには影響を与えません。
21.1.3 早期バッチ・フラッシュ
早期バッチ・フラッシュは、キャッシュされたメタデータが変更されると発生します。キャッシュされたメタデータは、次のような様々な理由から変更されることがあります。
-
最初のバインドがNULLで後続のバインドが非NULLである場合。
-
最初は文字列としてスカラー型がバインドされ、後でスカラー型としてバインドされた場合。あるいはその逆の場合。
早期バッチ・フラッシュ・カウントは、次のexecuteUpdate
メソッドまたはsendBatch
メソッドの戻り値に追加されます。
以前の機能では、ここで取得できるすべてのバッチ・フラッシュ値が失われました。以前の機能に切り替えるには、次に示すようにAccumulateBatchResult
プロパティをfalse
に設定します。
java.util.Properties info = new java.util.Properties(); info.setProperty("user", "HR"); info.setProperty("passwd", "hr"); // other properties ... // property: batch flush type info.setProperty("AccumulateBatchResult", "false"); OracleDataSource ods = new OracleDataSource(); ods.setConnectionProperties(info); ods.setURL("jdbc:oracle:oci:@""); Connection conn = ods.getConnection();
ノート:
AccumulateBatchResult
プロパティはデフォルトでtrue
に設定されます。
例21-2は、早期バッチ・フラッシュを示しています。
例21-2 早期バッチ・フラッシュ
((OraclePreparedStatement)pstmt).setExecuteBatch (2); pstmt.setNull(1, OracleTypes.NUMBER); pstmt.setString(2, "test11"); int count = pstmt.executeUpdate(); // returns 0 /* * Premature batch flush happens here. */ pstmt.setInt(1, 22); pstmt.setString(2, "test22"); int count = pstmt.executeUpdate(); // returns 0 pstmt.setInt(1, 33); pstmt.setString(2, "test33"); /* * returns 3 with the new batching scheme where as, * returns 2 with the old batching scheme. */ int count = pstmt.executeUpdate();
21.2 その他のOracleパフォーマンス拡張機能
Oracle JDBCドライバでは、バッチ更新に加えて、次の拡張機能がサポートされています。これにより、データベースへのラウンドトリップが減少し、パフォーマンスが向上します。
-
行をプリフェッチすることにより、データがフェッチされるたびに複数行がフェッチされるので、データベースへのラウンドトリップが減少します。余分にフェッチされたデータは、後で使用するためにクライアント側バッファに格納されます。プリフェッチの行数は、目的に応じて設定できます。
-
問合せの実行および問合せ結果の取出しを行うときに、標準のJDBCプロトコルで生じる非効率性を回避できます。
-
データベース・メタデータ
TABLE_REMARKS
列の抑止コストの高い外部結合操作を回避できます。
Oracleでは、これらのパフォーマンス拡張機能をサポートするために、接続プロパティ・オブジェクトにいくつかの拡張機能を提供します。これらの拡張機能により、remarksReporting
フラグと、行プリフェッチとバッチ更新のデフォルト値を設定できます。
この項の内容は次のとおりです。
21.2.1 LOBデータのプリフェッチについて
Oracle Database 11gリリース2 JDBCドライバより前のJDBCドライバの場合、1回のラウンドトリップでLOB
データを取得するには、データをVARCHAR2
型としてフェッチする必要があります。つまり、OracleTypes.VARCHAR
またはOracleTypes.LONGVARCHAR
を、JDBC defineColumnType
メソッドとともに使用する必要があります。この手法の制限は、LOB
データをCHAR
型としてフェッチする場合、ロケータをデータとともにフェッチすることができないということです。このため、アプリケーションがLOB
データを後から取得する場合、またはアプリケーションが他のLOB
操作を実行する場合、アプリケーションがLOB
ロケータを取得できないため、LOB
ロケータを取得するためにラウンドトリップがもう1回必要になります。
ノート:
LOB
ロケータでの配列操作は、JDBC APIでサポートされていません。
Oracle Database 11gリリース2以降のJDBCドライバの場合、通常のフェッチ操作中にロケータとともにLOB
データの始めをプリフェッチするのと同様に、LOB
の長さやチャンク・サイズなどの使用頻度の高いメタデータをプリフェッチすることで、ラウンドトリップの回数を減らします。LOB
が小さい場合、ただ1回のラウンドトリップでデータをすべてプリフェッチできる場合があります。select
、パース、実行およびフェッチが1回のラウンドトリップで行われるため、パフォーマンスが大幅に向上します。LOB
がプリフェッチ・サイズの5倍より大きい場合、チャンク・サイズを取得するためのラウンドトリップが必要ないだけなので、パフォーマンスはそれほど向上しません。
defaultLobPrefetchSize接続プロパティ
Oracle Database 11gリリース2から、接続のためのデフォルトLOB
プリフェッチ・サイズを設定するために使用できる新しい接続プロパティoracle.jdbc.defaultLobPrefetchSize
が導入されました。この接続プロパティは、定数OracleConnection.CONNECTION_PROPERTY_DEFAULT_LOB_PREFETCH_SIZE
として定義されています。このプロパティの値が、現行接続のデフォルトのLOB
プリフェッチ・サイズとして使用されます。この接続プロパティのデフォルト値は4000です。文レベルでこのデフォルト値を変更する場合は、oracle.jdbc.OracleStatement
インタフェースで定義されているsetLobPrefetchSize
メソッドを使用します。デフォルト値は、次の値に変更できます。
-
現行接続の
LOB
プリフェッチを無効にするには-1 -
メタデータ専用の
LOB
プリフェッチを有効にするには0 -
フェッチ操作中にロケータとともにプリフェッチする
BLOB
のバイト数およびCLOB
の文字数を指定するには、0より大きな任意の値
LOB
プリフェッチ・サイズを取り出すには、oracle.jdbc.OracleStatement
インタフェースで定義されているgetLobPrefetchSize
メソッドを使用します。
defineColumnType
メソッドを使用して列レベルでLOB
プリフェッチ・サイズの値も設定できます。列レベルの値は、接続または文レベルで設定されているどの値よりも優先されます。
関連項目:
詳細は、Javadocを参照してください。
ノート:
LOB
プリフェッチが接続レベルまたは文レベルで無効でない場合、列レベルでも無効にできません。
21.2.2 Oracle行プリフェッチの制限事項
最大のプリフェッチ量は設定されていません。デフォルト値は10です。問合せから予想される行数および列数に基づいて、値を増減させた方が適している場合もあります。Properties
オブジェクトを使用して、接続のデフォルト行プリフェッチ値を設定できます。
Statementオブジェクトは、作成されると、関連付けられた接続から行のプリフェッチ設定のデフォルト値を受け取ります。接続の行プリフェッチ設定のデフォルト値を後で変更しても、文の行プリフェッチ設定は変更されません。
結果セットの列のデータ型が、データ・インタフェースから戻されたLONG
、LONG
RAW
またはLOB
、つまりストリーム型の場合は、いずれの型の値も実際に読み取らなくても、文の行プリフェッチ設定はJDBCによって1に変更されます。
プリフェッチ・サイズの設定は、アプリケーションのパフォーマンスに影響を与える場合があります。プリフェッチ・サイズを大きくすると、全データの取得に必要なラウンドトリップ数は減りますが、メモリーの使用量は増えます。これは、問合せ内の列の数とサイズ、および戻されることが予想される行数に依存します。また、JDBCクライアント・マシンのメモリーおよびCPU負荷にも依存します。スタンドアロン・クライアント・アプリケーションの最適値は、負荷の大きいアプリケーション・サーバーの場合と異なります。ネットワーク接続の速度および待機時間も考慮してください。
ノート:
Oracle Database 11gリリース1以降、Thinドライバは、最初のラウンドトリップで、最初のprefetch_size
行をサーバーからフェッチすることができます。これにより、SELECT文でラウンドトリップを1回減らせます。
10gリリース1(10.1)以上のOracle JDBCドライバにそれより前のリリースのOracle JDBCドライバからアプリケーションを移行する場合は、以前検討した最適化を再検討する必要があります。メモリーの使用量およびパフォーマンス特性が大きく変化している場合があるからです。
一意なキーを選択する問合せなどは、よく実行する処理です。そのような問合せは、行をゼロ個または1つ戻します。プリフェッチ・サイズを1に設定すると、メモリーおよびCPUの使用量は減り、ラウンドトリップ数は増えません。if(rs.next())
のかわりにwhile(rs.next())
を記述して余分なフェッチを要求するというミスを避けるよう、注意する必要があります。
JDBC Thinドライバを使用する場合、useFetchSizeWithLongColumn
接続プロパティを使用してください。PARSE
、EXECUTE
およびFETCH
が単一のラウンドトリップ内で実行されるからです。
プリフェッチ・サイズのチューニングは、実際のアプリケーションの現実的な負荷に基づき、JVM内でのメモリー管理のチューニングに合せて行う必要があります。
ノート:
-
JDBC 2.0フェッチ・サイズApplication Program Interface(API)とOracle行プリフェッチAPIをアプリケーションで混在させることはできません。どちらも使用できますが、両方は使用できません。
-
Oracle行フェッチ・サイズ値を設定すると、問合せ以外に、結果セットの
refreshRow
メソッドによる結果セットの行の明示的な再フェッチ(scroll-sensitive/読取り専用、scroll-sensitive/更新可能およびscroll-insensitive/更新可能結果セットに関係します)行数およびscroll-sensitive結果セットのウィンドウ・サイズ(自動再フェッチの実行頻度に影響します)にも影響を与えることがあります。ただし、Oracle行フェッチ・サイズ値は、フェッチ・サイズの設定によってオーバーライドされます。
関連項目:
21.2.3 列型の定義について
ノート:
Oracle Database 12cリリース1 (12.1)以降、defineColumnType
メソッドは非推奨です。詳細は、「非推奨となった機能」を参照してください。
Oracle Database 10gでは、defineColumnType
の実装が大幅に変更されました。これまでは、defineColumnType
はパフォーマンスの最適化とデータ型変換の強制実行の両方に使用されていました。以前のリリースでは、すべてのドライバがdefineColumnType
のコールによる利点を得ていました。Oracle Database 10gから、JDBC Thinドライバはこの情報の提供を必要としなくなりました。JDBC Thinドライバは、defineColumnType
をコールせずに最大のパフォーマンスを実現します。アプリケーションでdefineColumnType
を使用すると、JDBC Oracle Call Interface(OCI)およびサーバー側内部ドライバのパフォーマンスが向上します。
コードでJDBC ThinドライバおよびJDBC OCIドライバの両方を使用する場合は、Thinドライバの使用時に接続プロパティdisableDefineColumnType
をtrue
に設定することで、defineColumnType
メソッドを無効にできます。その結果、defineColumnType
は無視されます。JDBC OCIまたはサーバー側内部ドライバを使用する場合は、この接続プロパティをtrue
に設定しないでください。
また、defineColumnType
を使用して、クライアント側が割り当てるメモリー量を制御することや、可変長データのサイズを制限することもできます。
-
必要に応じて、Statementオブジェクトを
OracleStatement
、OraclePreparedStatement
またはOracleCallableStatement
にキャストします。 -
必要に応じて、
Statement
オブジェクトのclearDefines
メソッドを使用して、このStatement
オブジェクトの以前の列定義を消去します。 -
各列で、
Statement
オブジェクトのdefineColumnType
メソッドをコールして次のパラメータを渡します。-
列索引(整数)
-
型コード(整数)
java.sql.Types
クラスまたはoracle.jdbc.OracleTypes
クラスのstatic
定数を使用します(Types.INTEGER
、Types.FLOAT
、Types.VARCHAR
、OracleTypes.VARCHAR
、OracleTypes.ROWID
など)。この2つのクラスで、標準型の型コードは同一です。 -
型名(文字列)
構造化オブジェクト、オブジェクト参照および配列の場合は、型名も指定する必要があります。たとえば、
Employee
、EmployeeRef
、EmployeeArray
などです。 -
最大フィールド・サイズ(整数)
オプションで、この列の最大データ長も指定できます。
構造化オブジェクト、オブジェクト参照または配列の列型を定義する場合、最大フィールド・サイズ・パラメータは指定できません。このパラメータを含めても、無視されます。
-
使用する形式(short)
オプションで、この列の使用する形式も指定できます。データベース文字セットを使用する場合は
OraclePreparedStatement.FORM_CHAR
に、各国語文字セットを使用する場合はOraclePreparedStatement.FORM_NCHAR
に設定します。このパラメータを省略した場合、デフォルトはFORM_CHAR
です。
たとえば、
stmt
をOracle文と仮定して、次のように使用します。stmt.defineColumnType(column_index, typeCode);
列が
VARCHAR
または等価な型で、長さの制限がわかっている場合は、次のようにします。stmt.defineColumnType(column_index, typeCode, max_size);
列が
NVARCHAR
で、元の最大長が必要、かつデータベース文字セットへの変換が要求される場合は、次のようにします。stmt.defineColumnType(column_index, typeCode, 0, OraclePreparedStatement.FORM_CHAR );
列が構造化オブジェクト、オブジェクト参照および配列の場合は、次のようにします。
stmt.defineColumnType(column_index, typeCode, typeName);
デフォルトのデータ長をすべて受け取る必要がない場合は、最大フィールド・サイズを設定します。標準JDBC
Statement
クラスのsetMaxFieldSize
メソッドをコールして、戻されるデータ量の制限を設定します。つまり、戻されるデータのサイズは、次の値の最小値になります。-
defineColumnType
で設定された最大フィールド・サイズ -
setMaxFieldSize
で設定された最大フィールド・サイズ -
データ型固有の最大サイズ
-
これらのステップを完了した後に、文のexecuteQuery
メソッドを使用して問合せを実行します。
ノート:
必要な結果セットのすべての列に対してデータ型を指定する必要はありません。
例21-3は、この機能の使用方法を示しています。この例では、oracle.jdbc.*
インタフェースがインポート済であることを前提にしています。
例21-3 列型の定義
OracleDataSource ods = new OracleDataSource(); ods.setURL("jdbc:oracle:thin:@localhost:5221:orcl"); ods.setUser("HR"); ods.setPassword("hr"); Connection conn = ods.getConnection(); Statement stmt = conn.createStatement(); // Allocate only 2 chars for this column (truncation will happen) ((OracleStatement)stmt).defineColumnType(1, Types.VARCHAR, 2); ResultSet rset = stmt.executeQuery("select ename from emp"); while(rset.next() ) System.out.println(rset.getString(1)); stmt.close();
この例が示すように、defineColumnType
メソッドの起動では、Statement
オブジェクトstmt
をOracleStatement
にキャストする必要があります。接続のcreateStatement
メソッドは、defineColumnType
メソッドとclearDefines
を持たず、型がjava.sql.Statement
であるオブジェクトを戻します。これらのメソッドは、OracleStatement
実装でのみ提供されています。
定義の拡張要素では、JDBC型を使用して目的の型を指定します。使用可能な列型は、列のOracle内部型によって異なります。
すべての列は、本来のJDBC型に定義できます。多くの場合、Types.CHAR
またはTypes.VARCHAR
型コードに定義できます。
表21-1に、defineColumnType
メソッドで使用できる有効な列定義引数を示します。
表21-1 有効な列型
列内のOracle SQL型 | defineColumnTypeで定義できる型 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defineColumnType
を列の元のデータ型で使用することは、常に可能です。
21.2.4 DatabaseMetaData TABLE_REMARKSのレポートについて
データベース・メタデータ・クラスのgetColumns
、getProcedureColumns
、getProcedures
およびgetTables
メソッドを使用してTABLE_REMARKS
列をレポートすると、コストの高い外部結合を必要とするため処理が遅くなります。この理由でJDBCドライバは、デフォルトではTABLE_REMARKS
列を報告しません。
OracleConnection
オブジェクトのsetRemarksReporting
メソッドにtrue
引数を渡すと、TABLE_REMARKS
レポートが使用可能になります。
Java Properties
オブジェクトを使用して接続を確立した場合、setRemarksReporting
をコールするかわりに、remarksReporting
Javaプロパティを設定することもできます。
標準java.sql.Connection
オブジェクトを使用している場合、setRemarksReporting
を使用するには、オブジェクトをOracleConnection
にキャストする必要があります。
次のコードでは、TABLE_REMARKS
レポートを使用可能にする方法を示します。
((oracle.jdbc.OracleConnection)conn ).setRemarksReporting(true);
ここで、conn
は標準Connection
オブジェクト名を表しています。次の文を使用するとTABLE_REMARKS
レポートが使用可能になります。
getColumnsの考慮事項
シノニムが指定されている場合、デフォルトでは、getColumns
メソッドは列の情報を取り出しません。シノニムが指定されている場合に情報の取出しを可能にするには、次のように、接続でsetIncludeSynonyms
メソッドをコールする必要があります。
((oracle.jdbc.OracleConnection)conn ).setIncludeSynonyms(true)
これによって、その接続での後続のgetColumns
メソッドのコールすべてにシノニムが含まれます。これは、setRemarksReporting
と同じです。あるいは、includeSynonyms
接続プロパティを設定できます。これは、remarksReporting
接続プロパティと同じです。
ただし、includeSynonyms
がtrue
に設定されている場合、シノニムが存在すると、table_name
列に戻されるオブジェクト名は、シノニム名であることに留意してください。これは、表名をgetColumns
に渡す場合でも同じです。
getProceduresおよびgetProcedureColumnsメソッドの考慮事項
JDBCバージョン1.1および1.2では、getProcedures
およびgetProcedureColumns
メソッドは、catalog
、schemaPattern
、columnNamePattern
およびprocedureNamePattern
パラメータを同じ方法で処理します。これらのメソッドに関するOracleの定義では、パラメータの処理方法は次のように異なります。
-
catalog
Oracleには複数カタログはありませんが、複数パッケージはあります。したがって、
catalog
パラメータは、パッケージ名として扱われます。これは入力(catalog
パラメータ)と出力(戻されたResultSet
のcatalog
列)の両方に該当します。入力時に、構成"
"
(空の文字列)は、プロシージャと引数を、パッケージ(スタンドアロンのオブジェクト)なしで取得します。null
値は、選択基準(スタンドアロン・オブジェクトとパッケージ化されたオブジェクトの両方に関する戻し情報)から除外されることを意味します。つまり、パーセント記号(%
)を渡すのと同じ効果です。それ以外の場合は、catalog
パラメータには、パッケージ名パターンを指定します。必要に応じてSQLワイルドカードを付けられます。 -
schemaPattern
Oracle Database内のオブジェクトはすべてはスキーマを持つ必要があるため、スキーマを持たないオブジェクトの情報を戻すことには意味がありません。このため、構成
"
"
(空の文字列)は、入力時に、現在のスキーマ、つまり現在接続しているスキーマを持つオブジェクトを意味するものと解釈されます。catalog
パラメータの動作との整合性を取るため、null
は選択基準からスキーマを除外するものと解釈されます。つまり、%
を渡すのと同じ効果です。SQLワイルドカードを使用したパターンとしても使用できます。 -
procedureNamePattern
およびcolumnNamePattern
プロシージャと引数はすべて名前を持っているため、空の文字列(" ")はいずれのパラメータについても意味を持ちません。このため、構成
"
"
を使用すると例外が発生します。他のパラメータの動作との整合性を取るため、null
はパーセント記号(%
)を渡すのと同じ効果を持ちます。