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

前へ
前へ
次へ
次へ
 

25 パフォーマンス拡張要素

この章では、Java Database Connectivity(JDBC)標準に対するOracleパフォーマンス拡張要素について説明します。

次の項目が含まれます。

バッチ更新

複数のUPDATEDELETEまたはINSERT文を単一のバッチにグループ化し、バッチ全体をデータベースに送信して1回のラウンドトリップで処理することにより、データベースへのラウンドトリップの回数を削減できます。その結果、アプリケーションのパフォーマンスが向上します。これを、バッチ更新(update batching)と呼びます。


注意:

JDBC 2.0仕様では、これをバッチ更新(batch updates)と呼びます。

これは、特にプリコンパイルされたSQL文で、同じ文をバインド変数を変えて繰り返し使用する場合に効果的です。

Oracle JDBCでは、2つの異なるバッチ更新のモデルがサポートされています。


注意:

これらのモデルは混在させることができません。単一アプリケーションで、どちらのモデルを使用してもかまいませんが、両方は使用できません。これらを混在させると、Oracle JDBCドライバで例外が発生します。

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

バッチ更新モデルの概要

この項では、一般的なモデルとサポートされる文の種類を、標準バッチ更新とOracleバッチ更新とで比較します。

Oracleモデルと標準モデル

Oracleバッチ更新では、バッチ値を使用することにより、通常、暗黙的なバッチの処理が発生します。バッチ値とは、データベースへのラウンドトリップを発生させるまでにバッチに追加する操作の数です。この値まで操作がバッチに追加されると、バッチが処理されます。次のことに注意してください。

  • 接続オブジェクトにデフォルト・バッチ値を設定できます。このバッチ値は、その接続で実行されるプリコンパイルされたSQL文すべてに適用されます。

  • プリコンパイルされたSQL文オブジェクトには、個別に文バッチ値を設定できます。この値は、接続バッチ値より優先します。

  • 接続バッチ値と文バッチ値の両方を無視して、いつでも明示的にバッチを処理できます。

標準のバッチ更新は、手動の明示的なモデルです。バッチ値は設定されていません。手動で操作をバッチに追加し、明示的にバッチの処理を指定します。

Oracleバッチ更新のほうが効率的なモデルです。Oracleバッチ更新では、ドライバが、バッチされる操作の数をあらかじめ知ることができます。この意味で、Oracleモデルのほうが静的で予測可能です。標準モデルでは、ドライバがバッチされる操作の数をあらかじめ知ることはできません。この意味で、標準モデルのほうが本質的に動的です。

バッチ更新を使用する場合は、次の基準で2つのモデルから1つを選択できます。

  • 移植性が重要でない場合は、Oracleバッチ更新を使用します。これにより、最高のパフォーマンス向上が得られます。

  • パフォーマンスよりも移植性の優先順位が高い場合は、標準バッチ更新を使用します。

サポートされている文の型

Oracleによる実装のとおり、バッチ更新は、同じ文をバインド変数を変えて繰り返し使用する場合に、プリコンパイルされたSQL文で使用するように用意されています。次のことに注意してください。

  • Oracleバッチ更新でサポートされるのは、プリコンパイルされたSQL文オブジェクトのみです。コール可能文では、接続デフォルト・バッチ値と文バッチ値の両方が値1にオーバーライドされます。Oracleの一般的な文では、文バッチ値はなく、接続デフォルト・バッチ値は値1にオーバーライドされます。

  • JDBC 2.0標準に準拠するために、標準バッチ更新のOracle実装では、プリコンパイルされたSQL文と同じように、OUTパラメータなしのコール可能文および一般的な文もサポートされています。標準バッチ更新は、Oracle JDBCアプリケーションに簡単に移行できます。

  • バッチできるのは、UPDATEINSERTまたはDELETE操作のみです。結果セットを戻そうとする操作を含むバッチを処理すると、例外が発生します。


    注意:

    標準バッチ更新のOracle実装では、一般的な文およびコール可能文の実際のバッチ処理は実装されていません。Oracle JDBCはStatementおよびCallableStatementオブジェクトに対する標準バッチ処理構文の使用をサポートしますが、パフォーマンスが向上するのはPreparedStatementオブジェクトの場合のみです。

Oracleバッチ更新

Oracleバッチ更新によって、バッチ値がプリコンパイルされたSQL文オブジェクトに対応付けられます。Oracleバッチ更新を使用すると、JDBCドライバはexecuteUpdateメソッドがコールされるたびにプリコンパイルされたSQL文を実行するのではなく、蓄積された処理要求のバッチに文を追加します。バッチ値に達すると、ドライバはすべての操作をデータベースに渡して一度に処理します。たとえば、バッチ値が10の場合、10の操作がバッチに蓄積されるたびにデータベースに送信され、1回のラウンドトリップで処理されます。

OracleConnectionクラスのメソッドにより、Oracle接続全体のデフォルト・バッチ値を設定できます。このバッチ値は、その接続でのOracle PreparedStatementすべてに適用されます。OraclePreparedStatementクラスのメソッドにより、特定のOracle PreparedStatementに文バッチ値を設定できます。この値は、接続バッチ値より優先します。手動で保留バッチを処理することにより、両方のバッチ値をオーバーライドすることもできます。


注意:

  • 標準更新バッチとOracle更新バッチを同じアプリケーションに混在させることはできません。これらを混在させると、JDBCドライバで例外が発生します。

  • どちらかのバッチ更新モデルを使用する場合、自動コミット・モードは無効化します。バッチの処理中にエラーが発生した場合、エラーの前に正常に実行された操作をコミットするかロールバックするかを選択できます。


Oracleバッチ更新の特徴と制限事項

Oracleバッチ更新に関する、次の制限事項と実装の詳細に注意してください。

  • デフォルトでは、文バッチ値はなく、接続バッチ値は1です。

  • バッチ値は、5〜30にすると最も効率的です。非常に高い値を設定すると、逆効果になります。特定のアプリケーションでの有効性を検証するために、様々な値を試すことをお薦めします。

  • バッチ値が有効かどうかにかかわらず、Oracle PreparedStatementのバインド変数がストリーム型である場合、Oracle JDBCドライバによりバッチ値は1に設定され、処理待ちの要求はすべてデータベースに送信され、処理されます。

  • Oracle JDBCドライバは、次の場合に、Oracle PreparedStatementのsendBatchメソッドを自動的に実行します。

    • 接続が、commitメソッドのコールまたは自動コミット・モードの結果として、COMMIT要求を受信した場合。

    • 文が、close要求を受信した場合。

    • 接続が、close要求を受信した場合。


    注意:

    接続COMMIT要求、文クローズまたは接続クローズは、Oracle更新バッチを使用する場合のみ、保留バッチに影響を与えます。ただし、標準バッチ更新を使用する場合は、保留バッチに影響を与えません。

  • sendBatchがコールされる前に接続がROLLBACK要求を受信した場合は、保留バッチ処理が削除されません。削除するには、clearBatchを明示的にコールする必要があります。

接続バッチ値の設定

Oracle接続では、任意のOracle PreparedStatementにデフォルト・バッチ値を指定できます。この値を指定するには、OracleConnectionオブジェクトのsetDefaultExecuteBatchメソッドを使用します。たとえば、次のコードにより、conn接続オブジェクトに対応付けられているすべてのプリコンパイルされたSQL文に対して、デフォルト・バッチ値が20に設定されます。

((OracleConnection)conn).setDefaultExecuteBatch(20);

このメソッドにより、その接続のプリコンパイルされたSQL文すべてに対してデフォルト・バッチ値が設定されますが、Oracle PreparedStatementで個別にsetDefaultBatchをコールすると、その設定値をオーバーライドできます。

接続バッチ値は、このバッチ値がセットされた後で作成された文オブジェクトに適用されます。

Java Propertiesオブジェクトを使用して接続を確立した場合、setDefaultExecuteBatchをコールするかわりに、defaultBatchValue Javaプロパティを設定することもできます。

文バッチ値の設定

特定のOracle PreparedStatementに文バッチ値を設定するには、次の手順を使用します。これにより、文を処理する接続のOracleConnectionインスタンスのsetDefaultExecuteBatchメソッドを使用して設定した接続バッチ値はすべて、オーバーライドされます。

  1. 次のように、プリコンパイルされたSQL文を記述し、第1行の入力値を指定します。

    PreparedStatement ps = conn.prepareStatement
                                  ("INSERT INTO dept VALUES (?,?,?)");
    ps.setInt (1,12);
    ps.setString (2,"Oracle");
    ps.setString (3,"USA");
    
    
  2. プリコンパイルされたSQL文をOraclePreparedStatementにキャストし、setExecuteBatchメソッドを適用します。この例では、文のバッチ・サイズは2に設定されます。

    ((OraclePreparedStatement)ps).setExecuteBatch(2);
    
    

    必要に応じて、プログラムの任意の場所に、次のようにgetExecuteBatchメソッドを挿入すると、文のデフォルト・バッチ値を確認できます。

    System.out.println (" Statement Execute Batch Value " +
                       ((OraclePreparedStatement)ps).getExecuteBatch());
    
    
  3. この時点で更新を実行するコールをデータベースに送信しても、データはデータベースに送信されず、0(ゼロ)が戻されます。

    // No data is sent to the database by this call to executeUpdate
    System.out.println ("Number of rows updated so far: "
                                      + ps.executeUpdate ());
    
    
  4. 第2行の入力値の集合および更新の実行を入力すると、executeUpdateのバッチ・コール回数がバッチ値2と等しくなります。データはデータベースに送信され、1回のラウンドトリップで2つの行が挿入されます。

    ps.setInt (1, 11);
    ps.setString (2, "Applications");
    ps.setString (3, "Indonesia");
    
    int rows = ps.executeUpdate ();
    System.out.println ("Number of rows updated now: " + rows);
    
    ps.close ();
    

バッチ値のチェック

Oracle接続インスタンスの全体的な接続バッチ値をチェックするには、OracleConnectionクラスのgetDefaultExecuteBatchメソッドを使用します。

Integer batch_val = ((OracleConnection)conn).getDefaultExecuteBatch();

Oracle PreparedStatementの特定の文バッチ値をチェックするには、OraclePreparedStatementクラスのgetExecuteBatchメソッドを使用します。

Integer batch_val = ((OraclePreparedStatement)ps).getExecuteBatch();


注意:

文バッチ値が設定されていない場合、getExecuteBatchは接続バッチ値を戻します。

バッチ値のオーバーライド

有効なバッチ値に達する前に、蓄積された操作を処理するには、OraclePreparedStatementオブジェクトのsendBatchメソッドを使用します。

この例では、接続バッチ値を20に設定します。これにより、接続に対応付けられているすべてのプリコンパイルされたSQL文オブジェクトのデフォルト・バッチ値は、20に設定されます。次のように、接続をOracleConnectionにキャストし、接続にsetDefaultExecuteBatchメソッドを適用します。

((OracleConnection)conn).setDefaultExecuteBatch (20);

次のように、バッチ値をオーバーライドします。

  1. 次のように、プリコンパイルされたSQL文を記述し、第1行の入力値を指定して、この文を処理します。

    PreparedStatement ps =
       conn.prepareStatement ("insert into dept values (?, ?, ?)");
    
    ps.setInt (1, 32);
    ps.setString (2, "Oracle");
    ps.setString (3, "USA");
    
    System.out.println (ps.executeUpdate ());
    
    

    この時点では、バッチは処理されません。ps.executeUpdateメソッドにより0が戻されます。

  2. 2番目の操作の入力値の集合を入力し、もう一度executeUpdateをコールしても、データはデータベースに送信されません。文に対して有効なバッチ値は、接続のバッチ値である20です。

    ps.setInt (1, 33);
    ps.setString (2, "Applications");
    ps.setString (3, "Indonesia");
    
    // this batch is still not executed at this point
    int rows = ps.executeUpdate ();
    
    System.out.println ("Number of rows updated before calling sendBatch: "
                            + rows);
    
    

    println文のrowsの値が0である点に注意してください。

  3. この時点でsendBatchを適用すると、これまでにバッチされていた2つの操作が、1回のラウンドトリップでデータベースに送信されます。sendBatchメソッドからは、更新された行の合計数も戻されます。sendBatchのプロパティをprintlnで使用すると、更新された行数が出力されます。

    // Execution of both previously batched executes will happen
    // at this point. The number of rows updated will be
    // returned by sendBatch.
    rows = ((OraclePreparedStatement)ps).sendBatch ();
    
    System.out.println ("Number of rows updated by calling sendBatch: "
                            + rows);
    ps.close ();
    
    

Oracleバッチ処理による変更のコミット

バッチを処理した後、変更をコミットする必要があります。自動コミットは使用禁止(推奨)にしてあるものとします。

Oracleバッチ処理で接続オブジェクトのcommitをコールすると、処理されたバッチの操作がコミットされるのみでなく、暗黙的なsendBatchコールが発行され、保留バッチがすべて処理されます。このように、commitによって、バッチに追加されたすべての操作による変更が効率的にコミットされます。

Oracleバッチ処理による更新件数

バッチを使用しない場合、OraclePreparedStatementオブジェクトのexecuteUpdateメソッドからは、操作の影響を受けたデータベース行の数が戻されます。

Oracleバッチ処理を使用する場合、このメソッドからは、次のように、メソッドが起動されたときに影響を受けた行の数が返されます。

  • executeUpdateのコールによって操作がバッチに追加される場合、値0(ゼロ)が戻されます。その時点では、データベースには何も書き込まれません。

  • executeUpdateのコールによってバッチ値に達し、バッチが処理される場合、バッチに含まれるすべての操作によって影響を受ける行の合計数が戻されます。

同様に、OraclePreparedStatementオブジェクトのsendBatchメソッドからは、バッチに含まれるすべての操作によって影響を受ける行の合計数が戻されます。

例25-1に、Oracleバッチ更新の使用方法を示します。

例25-1 Oracleバッチ更新

次の例では、Oracleバッチ更新機能の使用方法を示します。 この例では、oracle.driver.*インタフェースがインポート済であることを前提にしています。

...
OracleDataSource ods = new OracleDataSource();
ods.setURL("jdbc:oracle:oci);
ods.setUser("scott");
ods.setPassword("tiger");

Connection conn = ods.getConnection();
conn.setAutoCommit(false);

PreparedStatement ps =
  conn.prepareStatement("insert into dept values (?, ?, ?)");

//Change batch size for this statement to 3
((OraclePreparedStatement)ps).setExecuteBatch (3);

ps.setInt(1, 23);
ps.setString(2, "Sales");
ps.setString(3, "USA");
ps.executeUpdate(); //JDBC queues this for later execution

ps.setInt(1, 24);
ps.setString(2, "Blue Sky");
ps.setString(3, "Montana");
ps.executeUpdate(); //JDBC queues this for later execution

ps.setInt(1, 25);
ps.setString(2, "Applications");
ps.setString(3, "India");
ps.executeUpdate(); //The queue size equals the batch value of 3
                    //JDBC sends the requests to the database

ps.setInt(1, 26);
ps.setString(2, "HR");
ps.setString(3, "Mongolia");
ps.executeUpdate(); //JDBC queues this for later execution

((OraclePreparedStatement)ps).sendBatch(); // JDBC sends the queued request
conn.commit();

ps.close();
...


注意:

バッチによって遅延した更新は、他の問合せの結果に影響を与えることがあります。次の例では、バッチにより最初の問合せが遅延した場合は、次の問合せから予期しない結果が戻されます。
UPDATE emp SET name = "Sue" WHERE name = "Bob";
SELECT name FROM emp WHERE name = "Sue";

標準バッチ更新

JDBC 2.0仕様に従った標準バッチ更新モデルが実装されています。

このモデルは、Oracleバッチ更新モデルと異なり、addBatchメソッドを使用して明示的に文をバッチに追加し、executeBatchメソッドを使用して明示的にバッチを処理します。Oracleモデルでは、バッチ処理を行わない場合と同様にexecuteUpdateをコールしますが、操作をバッチに追加するか、バッチ全体を処理するかは、通常、あらかじめ定義されているバッチ値に達したかどうかによって、暗黙的に判断されます。


注意:

  • 標準更新バッチとOracle更新バッチを同じアプリケーションに混在させることはできません。これらを混在させると、Oracle JDBCドライバで例外が発生します。

  • どちらかのバッチ更新モデルを使用する場合、自動コミット・モードは無効化します。バッチの処理中にエラーが発生した場合、エラーの前に正常に実行された操作をコミットするかロールバックするかを選択できます。


標準バッチ処理のOracle実装の制限事項

標準バッチ更新のOracle実装に関する、次の制限事項と実装の詳細に注意してください。

  • Oracle JDBCアプリケーションでバッチ更新を使用すると、バインド変数の設定を変えてプリコンパイルされたSQL文を繰り返し処理できます。

    標準バッチ更新のOracle実装では、一般的な文およびコール可能文の実際のバッチ処理は実装されていません。Oracle JDBCはStatementおよびCallableStatementオブジェクトに対する標準バッチ処理の使用をサポートしますが、パフォーマンスは向上しません。

  • 標準バッチ更新のOracle実装では、バインド値としてストリーム型はサポートされません。ストリーム型を使用しようとすると、例外が発生します。

バッチへの操作の追加

文オブジェクトが最初に作成されるとき、文バッチは空です。標準addBatchメソッドを使用して、操作を文バッチに追加します。このメソッドは、標準java.sql.StatementPreparedStatementおよびCallableStatementインタフェースで指定されています。これらのインタフェースはそれぞれoracle.jdbc.OracleStatementOraclePreparedStatementおよび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つの操作が入っています。


注意:

ただし、標準バッチ更新のOracle実装では、一般的な文をバッチ処理してもパフォーマンスは向上しません。

プリコンパイルされたSQL文の場合、バッチ更新を使用して、バインド・パラメータの設定が異なる同じ文の複数の実行をバッチ処理します。PreparedStatementまたはOraclePreparedStatementオブジェクトの場合、addBatchメソッドは入力を取りません。適切なsetXXXメソッドで最後に設定されたバインド・パラメータを使用して、操作をバッチに追加するのみです。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つのバッチは単一のPreparedStatementオブジェクトに対応付けられるので、バッチ処理できるのは、この例のような単一のプリコンパイルされたSQL文の繰返し実行のみです。

バッチの処理

操作のカレント・バッチを処理するには、文オブジェクトのexecuteBatchメソッドを使用します。このメソッドは、標準Statementインタフェースで指定されています。このインタフェースは、標準PreparedStatementおよびCallableStatementインタフェースによって拡張されます。

次の例では、前の例で示したプリコンパイルされたSQL文の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();
...

executeBatchメソッドからは、int配列が戻されます。通常、1つの要素がバッチ処理された1つの操作に対応し、バッチの処理が成功したか失敗したかを示します。影響を受けた行数に関する情報が含まれていることもあります。


注意:

  • addBatchをコールした後、executeUpdateをコールする前に、executeBatchまたはclearBatchをコールする必要があります。コールしないと、SQL例外が発生します。

  • バッチが処理されるとき、操作は、バッチに入れられた順に実行されます。

  • 文のバッチは、executeBatchが戻ると、空にリセットされます。

  • 接続がROLLBACK要求を受信すると、文のバッチが空にリセットされません。リセットするには、clearBatchを明示的にコールする必要があります。

  • 文オブジェクトのカレント結果セットがある場合、この結果セットはexecuteBatchコールによってクローズされます。


標準バッチ処理のOracle実装による変更のコミット

バッチを処理した後、変更をコミットする必要があります。自動コミットは使用禁止(推奨)にしてあるものとします。

commitをコールすると、バッチ処理されていない操作がコミットされ、処理済の文バッチのバッチ処理された操作がコミットされます。しかし、標準バッチ処理のOracle実装では、処理されていない保留文バッチは影響を受けません。

バッチの消去

操作のカレント・バッチを処理せずに消去するには、文オブジェクトのclearBatchメソッドを使用します。このメソッドは、標準Statementインタフェースで指定されています。このインタフェースは、標準PreparedStatementおよびCallableStatementインタフェースによって拡張されます。

次の例では、前の例で示したプリコンパイルされたSQL文の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();
   ...
}


注意:

  • addBatchをコールした後、executeUpdateをコールする前に、executeBatchまたはclearBatchをコールする必要があります。コールしないと、SQL例外が発生します。

  • clearBatchをコールすると、文バッチは空にリセットされます。

  • clearBatchメソッドの戻り値はありません。


標準バッチ処理のOracle実装の更新件数

文バッチが正常に処理された場合、文のexecuteBatchコールから戻される整数配列、つまり、更新件数配列には、常にバッチ操作1つに対して1つの要素が含まれます。標準バッチ更新のOracle実装では、配列要素の値は次のようになります。

  • プリコンパイルされたSQL文のバッチの場合、バッチに含まれている個々の文によって影響を受けたデータベースの行数はわかりません。そのため、配列要素の値はすべて-2になります。JDBC 2.0仕様によれば、値-2は、操作は正常終了したが影響を受けた行数は不明であることを示します。

  • 一般的な文のバッチの場合、配列には、各操作で影響を受けた行数を示す実際の更新件数を格納します。標準バッチのOracle実装では、一般的な文の場合のみ、実際の更新件数がわかります。

  • コール可能な文のバッチの場合、サーバーは、各操作で影響を受けた行数に関係なく、更新件数として常に値1を戻します。

コードの側では、バッチの正常な処理に対して、配列要素に-21または実際の更新件数のいずれが設定されても処理できるように準備しておく必要があります。正常なバッチ処理では、配列にはすべて-2が含まれるか、すべて1が含まれるか、またはすべて正の整数が含まれます。

例25-2に、標準更新バッチの使用方法を示します。

例25-2 標準更新バッチ

この例は、前の項のサンプル・コードを組み合せたもので、次の処理を行います。

  1. 自動コミット・モードの無効化。どちらかのバッチ更新モデルを使用する場合、無効にする必要があります。

  2. PreparedStatementオブジェクトの作成。

  3. PreparedStatementオブジェクトに対応付けられたバッチへの操作の追加。

  4. バッチの処理。

  5. バッチの操作のコミット。

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();
...

更新件数配列を処理して、バッチが正常に処理されたかどうかを判断できます。

標準バッチ処理のOracle実装のエラー処理

executeBatchがコールされたとき、バッチ処理された操作のうち1つでも失敗したり、結果セットを戻そうとすると、処理は停止し、java.sql.BatchUpdateExceptionが生成されます。

バッチ例外の後、BatchUpdateExceptionオブジェクトのgetUpdateCountsメソッドを使用して、更新件数配列を取り出せます。このメソッドからは、executeBatchメソッドと同じように、更新件数のint配列が戻されます。標準バッチ更新のOracle実装では、バッチが処理された後の更新件数配列の内容は次のようになります。

  • プリコンパイルされたSQL文のバッチの場合、どの操作が失敗したかはわかりません。配列には、バッチの操作1つに対して1つの要素が含まれ、各要素には値-3が設定されます。JDBC 2.0仕様によれば、値-3は、操作が正常終了しなかったことを示します。この場合、実際に失敗した操作は1つのみと考えられます。しかし、JDBCドライバは、どの操作が失敗したかわからないので、バッチ処理された操作をすべて、失敗したものとしてラベル付けします。

    この場合、必ず、ROLLBACK操作を実行する必要があります。

  • 一般的な文のバッチまたはコール可能文のバッチの場合、更新件数配列は、エラーの時点までの実際の更新件数を格納する、部分的な配列になります。標準バッチ更新のOracle実装では、Oracle JDBCは一般の文およびコール可能文の本当の意味でのバッチ処理を使用できないので、実際の更新件数がわかります。

    たとえば、バッチに20の操作が含まれているとき、最初の13は正常終了し、14番目で例外が生成された場合、更新件数配列には13の要素が含まれ、正常終了した操作の実際の更新件数が設定されます。

    この場合、正常終了した操作をコミットすることも、ロールバックすることもできます。

コードの側では、例外が発生した場合、バッチの失敗した処理に対して、配列要素に-3または実際の更新件数のどちらが設定されても処理できるように準備しておく必要があります。失敗したバッチ処理では、すべてに-3が含まれる完全な配列か、正の整数が含まれる部分配列が作成されます。

バッチ処理される文とバッチ処理されない文の混在

文オブジェクトに操作の保留バッチがある場合、通常のバッチ処理されない操作の処理を行うために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要求は、バッチ処理されない操作すべてと、処理済バッチの正常な操作すべてに影響を与えますが、保留バッチには影響を与えません。

早期バッチ・フラッシュ

早期バッチ・フラッシュは、キャッシュされたメタデータが変更されると発生します。キャッシュされたメタデータは、次のような様々な理由から変更されることがあります。

  • 最初のバインドがNULLで後続のバインドが非NULLである場合。

  • 最初は文字列としてスカラー型がバインドされ、後でスカラー型としてバインドされた場合。あるいはその逆の場合。

早期バッチ・フラッシュ・カウントは、次のexecuteUpdateメソッドまたはsendBatchメソッドの戻り値に追加されます。

以前の機能では、ここで取得できるすべてのバッチ・フラッシュ値が失われました。以前の機能に切り替えるには、次に示すようにAccumulateBatchResultプロパティをfalseに設定します。

java.util.Properties info = new java.util.Properties();
info.setProperty("user", "SCOTT");
info.setProperty("passwd", "TIGER");
// 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に設定されます。

例25-3に、早期バッチ・フラッシュを示します。

例25-3 早期バッチ・フラッシュ

((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 ();

その他のOracleパフォーマンス拡張要素

Oracle JDBCドライバでは、バッチ更新に加えて、次の拡張要素がサポートされています。これにより、データベースへのラウンドトリップが減少し、パフォーマンスが向上します。

Oracleでは、これらのパフォーマンス拡張要素をサポートするために、接続プロパティ・オブジェクトにいくつかの拡張要素を提供します。これらの拡張要素により、remarksReportingフラグと、行プリフェッチとバッチ更新のデフォルト値を設定できます。

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

Oracle行プリフェッチ

Oracle JDBCドライバには、問合せの間結果セットが移入される際に、クライアントにプリフェッチしてくる行数を設定できるようにする拡張要素が含まれています。この機能を使用すると、サーバーへのラウンドトリップ回数を減らすことができます。

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


注意:

フェッチ・サイズをプリセットする機能は、JDBC 2.0では標準機能になりました。

Oracleプリフェッチ値の設定

標準JDBCでは、結果セットを一度に1行ずつしか受け取れないため、そのたびにデータベースまでのラウンドトリップが必要になります。行のプリフェッチ機能は、整数の行プリフェッチ設定を指定された文オブジェクトに対応付けます。JDBCは、問合せ時にここで設定した複数の行を一度にフェッチします。つまり、プリフェッチ設定がnのときは、JDBCは問合せ条件と一致するn行をフェッチし、すべての行が一度でクライアントに戻されます。次に、nextコールが、このn行を処理し終わると、JDBCによって、問合せ条件に一致する次のn行がフェッチされます。

特定のOracle文に対して、プリフェッチする行数を設定できます。ある接続におけるすべての文に対してプリフェッチされるデフォルトの行数を、リセットすることもできます。クライアントにプリフェッチされるデフォルト行数は、10です。

次の方法で、特定の文でプリフェッチする行数を設定します。

  1. 必要に応じて、StatementオブジェクトをOracleStatementOraclePreparedStatementまたはOracleCallableStatementにキャストします。

  2. StatementオブジェクトのsetRowPrefetchメソッドを使用して、プリフェッチする行数を指定し、その値を整数として渡します。現在設定されているプリフェッチ数を確認するには、getRowPrefetchメソッドを使用します。このメソッドでは整数が戻されます。

次の方法で、ある接続におけるすべての文に対してプリフェッチするデフォルトの行数を設定します。

  1. ConnectionオブジェクトをOracleConnectionにキャストします。

  2. プリフェッチするデフォルト行数を設定するOracleConnectionオブジェクトのsetDefaultRowPrefetchメソッドを使用し、デフォルトに設定する整数を渡します。現在設定されているデフォルト値を確認する場合は、OracleConnectionオブジェクトのgetDefaultRowPrefetchメソッドを使用します。このメソッドでは整数が戻されます。

    Java Propertiesオブジェクトを使用して接続を確立した場合、setDefaultRowPrefetchをコールするかわりに、defaultRowPrefetch Javaプロパティを設定することもできます。


注意:

  • JDBC 2.0フェッチ・サイズApplication Program Interface(API)とOracle行プリフェッチAPIをアプリケーションで混在させることはできません。どちらも使用できますが、両方は使用できません。

  • Oracle行プリフェッチ値を設定すると、問合せ以外に、結果セットのrefreshRowメソッドによる結果セットの行の明示的な再フェッチ(scroll-sensitive/読取り専用、scroll-sensitive/更新可能およびscroll-insensitive/更新可能結果セットに関係します)行数およびscroll-sensitive結果セットのウィンドウ・サイズ(自動再フェッチの実行頻度に影響します)にも影響を与えることがあります。ただし、Oracle行プリフェッチ値は、フェッチ・サイズの設定によってオーバーライドされます。


例25-4に、行のプリフェッチを示します。

例25-4 行のプリフェッチ

行のプリフェッチ機能の使用例を示します。 この例では、oracle.jdbc.*インタフェースがインポート済であることを前提にしています。

OracleDataSource ods = new OracleDataSource();ods.setURL("jdbc:oracle:oci:");ods.setUser("scott");ods.setPassword("tiger");Connection conn = ods.getConnection();

//Set the default row-prefetch setting for this connection
((OracleConnection)conn).setDefaultRowPrefetch(7);

/* The following statement gets the default row-prefetch value for
   the connection, that is, 7.
 */
Statement stmt = conn.createStatement();

/* Subsequent statements look the same, regardless of the row
   prefetch value. Only execution time changes.
 */
ResultSet rset = stmt.executeQuery("SELECT ename FROM emp");
System.out.println( rset.next () );

while( rset.next () )
    System.out.println( rset.getString (1) );

//Override the default row-prefetch setting for this statement
( (OracleStatement)stmt ).setRowPrefetch (2);

ResultSet rset = stmt.executeQuery("SELECT ename FROM emp");
System.out.println( rset.next () );

while( rset.next() )
   System.out.println( rset.getString (1) );

stmt.close();

Oracle行プリフェッチの制限事項

プリフェッチの設定に最大値はありませんが、経験値では10行が効果的です。50行を超えたプリフェッチの設定では、パフォーマンスの向上は得られません。接続のプリフェッチ行数のデフォルト値を設定しない場合は、10行がデフォルトになります。

Statementオブジェクトでは、作成時に対応付けられた接続から、行のプリフェッチ設定のデフォルト値を受け取ります。接続の行プリフェッチ設定のデフォルト値を後で変更しても、文の行プリフェッチ設定は変更されません。

結果セットの列のデータ型がLONGまたはLONG RAW(ストリーム型)の場合、どちらの型の値も、実際に読み込まなくても、文の行プリフェッチ設定はJDBCによって1に変更されます。

プロパティ・オブジェクトを使用して、接続のデフォルト行プリフェッチ値を設定できます。

列型の定義

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の使用時に接続プロパティdisableDefineColumnTypetrueに設定することで、defineColumnTypeメソッドを無効にできます。その結果、defineColumnTypeは無視されます。JDBC OCIまたはサーバー側内部ドライバを使用する場合は、この接続プロパティをtrueに設定しないでください。

また、defineColumnTypeを使用して、クライアント側が割り当てるメモリー量を制御することや、可変長データのサイズを制限することもできます。

問合せの列型を定義するには、次の手順を実行します。

  1. 必要に応じて、StatementオブジェクトをOracleStatementOraclePreparedStatementまたはOracleCallableStatementにキャストします。

  2. 必要に応じて、StatementオブジェクトのclearDefinesメソッドを使用して、このオブジェクトの以前の列定義を消去します。

  3. 各キャラクタ列で、StatementオブジェクトのdefineColumnTypeメソッドをコールして次のパラメータを渡します。

    • 列索引(整数)

    • 型コード(整数)

      java.sql.Typesクラスまたはoracle.jdbc.OracleTypesクラスのstatic定数を使用します(Types.INTEGERTypes.FLOATTypes.VARCHAROracleTypes.VARCHAROracleTypes.ROWIDなど)。この2つのクラスで、標準型の型コードは同一です。

    • 型名(文字列)

      構造化オブジェクト、オブジェクト参照および配列の場合は、型名も指定する必要があります。たとえば、EmployeeEmployeeRefEmployeeArrayなどです。

    • 最大フィールド・サイズ(整数)

      オプションで、この列の最大データ長も指定できます。

      構造化オブジェクト、オブジェクト参照または配列の列型を定義する場合、最大フィールド・サイズ・パラメータは指定できません。このパラメータを含めても、無視されます。

    • 使用する形式(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メソッドを使用して問合せを実行します。


注意:

必要な結果セットのすべての列に対してデータ型を指定する必要はありません。

例25-5に、この機能の使用方法を示します。 この例では、oracle.jdbc.*インタフェースがインポート済であることを前提にしています。

例25-5 列型の定義

OracleDataSource ods = new OracleDataSource();ods.setURL("jdbc:oracle:thin:@localhost:1502:orcl");ods.setUser("scott");ods.setPassword("tiger");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メソッドによりjava.sql.Statement型のオブジェクトが戻されます。このオブジェクトにはdefineColumnTypeおよびclearDefinesメソッドは設定されていません。これらのメソッドは、OracleStatement実装以外では提供されません。

定義の拡張要素では、JDBC型を使用して目的の型を指定します。使用可能な列型は、列のOracle内部型によって異なります。

すべての列は、本来のJDBC型に定義できます。多くの場合、Types.CHARまたはTypes.VARCHAR型コードに定義できます。

表25-1は、defineColumnTypeメソッドで使用できる有効な列定義引数のリストです。

表25-1 有効な列型

列内のOracle SQL型 defineColumnTypeで定義できる型

NUMBERVARNUM

BIGINTTINYINTSMALLINTINTEGERFLOATREALDOUBLENUMERICDECIMALCHARVARCHAR

CHARVARCHAR2

CHARVARCHAR

LONG

CHARVARCHARLONGVARCHAR

LONGRAW

LONGVARBINARYVARBINARYBINARY

RAW

VARBINARYBINARY

DATE

DATETIMETIMESTAMPCHARVARCHAR

ROWID

ROWID

BLOB

VARBINARYBINARY

CLOB

LONGCHARVARCHAR


defineColumnTypeを列の元のデータ型で使用することは、常に可能です。

DatabaseMetaData TABLE_REMARKSレポート

データベース・メタデータ・クラスのgetColumnsgetProcedureColumnsgetProceduresおよびgetTablesメソッドを使用してTABLE_REMARKS列をレポートすると、コストの高い外部結合を必要とするため処理が遅くなります。このため、このJDBCドライバは、デフォルトではTABLE_REMARKS列をレポートしません。

OracleConnectionオブジェクトのsetRemarksReportingメソッドにtrue引数を渡すと、TABLE_REMARKSレポートが使用可能になります。

Java Propertiesオブジェクトを使用して接続を確立した場合、setRemarksReportingをコールするかわりに、remarksReporting Javaプロパティを設定することもできます。

標準java.sql.Connectionオブジェクトを使用している場合、setRemarksReportingを使用するには、オブジェクトをOracleConnectionにキャストする必要があります。

例25-6に、TABLE_REMARKSレポートを使用可能にする方法を示します。

例25-6 TABLE_REMARKSレポート

connは標準Connectionオブジェクト名を表しています。次の文を使用するとTABLE_REMARKSレポートが使用可能になります。

( (oracle.jdbc.OracleConnection)conn ).setRemarksReporting(true);

getColumnsの考慮事項

シノニムが指定されている場合、デフォルトでは、getColumnsメソッドは列の情報を取り出しません。シノニムが指定されている場合に情報の取出しを可能にするには、次のように、接続でsetIncludeSynonymsメソッドをコールする必要があります。

( (oracle.jdbc.driver.OracleConnection)conn ).setIncludeSynonyms(true)

これによって、その接続での後続のgetColumnsメソッドのコールすべてにシノニムが含まれます。これは、setRemarksReportingと同じです。あるいは、includeSynonyms接続プロパティを設定できます。これは、remarksReporting接続プロパティと同じです。

ただし、includeSynonymstrueに設定されている場合、シノニムが存在すると、table_name列に戻されるオブジェクト名は、シノニム名であることに留意する必要があります。これは、表名をgetColumnsに渡す場合でも同じです。

getProceduresおよびgetProcedureColumnsメソッドの考慮事項

JDBCバージョン1.1および1.2では、getProceduresおよびgetProcedureColumnsメソッドは、catalogschemaPatterncolumnNamePatternおよびprocedureNamePatternパラメータを同じ方法で処理します。これらのメソッドに関するOracleの定義では、パラメータの処理方法は次のように異なります。

  • catalog

    Oracleには複数カタログはありませんが、複数パッケージはあります。このため、catalogパラメータはパッケージ名として処理されます。これは、入力(catalogパラメータ)および出力(戻されたResultSet内のcatalog列)の両方に適用されます。" "(空白文字列)を入力すると、パッケージなしのプロシージャおよび引数、つまり、スタンドアロン・オブジェクトが取り出されます。null値は、選択条件が削除されたことを意味します。つまり、スタンドアロン・オブジェクトとパッケージ・オブジェクトの情報の両方が戻されます。つまり、%を渡した場合と同じ効果を持ちます。その他の場合、catalogパラメータはパッケージ名のパターンになり、必要に応じて、SQLワイルド・カードを使用できます。

  • schemaPattern

    Oracleのすべてのオブジェクトは、スキーマを持ちます。したがって、スキーマを持たないオブジェクトの情報を戻すことは意味がありません。このため、入力時の" "(空白文字列)は、カレント・スキーマ(現在接続しているスキーマ)のオブジェクトと解釈されます。catalogパラメータの動作と一貫性を保つために、nullは選択条件からスキーマを削除すると解釈されます。つまり、%を渡した場合と同じ効果を持ちます。また、SQLワイルド・カードとともにパターンとしても使用できます。

  • procedureNamePatternおよびcolumnNamePattern

    空白文字列(" ")は、どちらのパラメータに対しても意味を持ちません。すべてのプロシージャおよび引数には名前が必要なためです。このため、" "では例外が発生します。他のパラメータの処理と一貫性を保つために、nullは、%を渡した場合と同じ効果を持ちます。