SQLJ アプリケーションでのバッチ更新の実行

これは、 IBM® Data Server Driver for JDBC and SQLJ SQLJでの一括更新をサポートしています。 表の各行を 1 行ずつ更新する代わりにバッチ更新を使用すれば、SQLJ によって一群の更新を同時に実行できます。

このタスクについて

バッチ更新には、以下のタイプのステートメントを組み込めます。
  • 検索の INSERT、UPDATE、または DELETE、または MERGE の各ステートメント
  • CREATE、ALTER、DROP、GRANT、REVOKE の各ステートメント
  • 入力パラメーターだけを指定した CALL ステートメント
JDBC の場合とは異なり、SQLJ では、入力パラメーターまたはホスト式を含んだステートメントを組み込んだ異種バッチを使用できます。 したがって、SQLJ バッチでは、以下の項目を自由に組み合わせることができます。
  • 同じステートメントの各インスタンス
  • 別々のステートメント
  • 異なる数の入力パラメーターまたはホスト式を含んだステートメント
  • 異なるデータ・タイプの入力パラメーターまたはホスト式を含んだステートメント
  • 入力パラメーターまたはホスト式がないステートメント

バッチによる挿入を除くすべての場合について、バッチ中のステートメントの実行中にエラーが発生すると、残りのステートメントが実行され、バッチ中のすべてのステートメントが実行された後で BatchUpdateException がスローされます。

バッチによる挿入の場合、動作は次のとおりです。

  • db2sqljcustomize を実行する際に、 atomicMultiRowInsert を DB2BaseDataSource.YES (1) に設定し、対象のデータサーバーが Db2 for z/OS® バッチ内のINSERT文の実行中にエラーが発生した場合、残りの文は実行されず、 BatchUpdateException がスローされます。
  • db2sqljcustomize を実行する際に、 atomicMultiRowInsert を DB2BaseDataSource.YES (1) に設定しない場合、または対象のデータサーバーが Db2 for z/OS バッチ内のINSERT文の実行中にエラーが発生した場合、残りの文は実行され、バッチ内のすべての文の実行後に BatchUpdateException がスローされます。

警告に関する情報を取得するには、バッチの中に組み込むステートメントをサブミットするのに使用する ExecutionContextExecutionContext.getWarnings メソッドを使用します。 これで、各 SQLWarning オブジェクトのエラーに関する記述、SQLSTATE、およびエラー・コードを取得できます。

バッチに追加できないステートメントがプログラムに含まれているために、バッチが暗黙的に実行される場合は、バッチが実行されてから、その新しいステートメントが処理されます。 バッチの実行中にエラーが発生すると、バッチ実行の原因になったステートメントは実行されません。

プロシージャー

ステートメント・バッチの作成、実行、削除の基本手順は、以下のとおりです。

  1. AutoCommit を接続から無効にします。

    そのようにしておくと、バッチの実行中にエラーが発生した場合に、既に実行されたステートメントの変更内容をコミットするかどうかを自分で制御できます。

  2. 実行コンテキストを取得します。

    バッチの中で実行するすべてのステートメントは、その実行コンテキストを使用します。

  3. ExecutionContext.setBatching(true) メソッドを呼び出してバッチを作成します。

    ステップ2で作成した実行コンテキストに関連付けられた、後続のバッチ可能なステートメントは、後で実行するためにバッチに追加されます。

    バッチ互換でないステートメント・セットを並列的にバッチに組み込む場合は、バッチ互換ステートメントの各セットごとに実行コンテキストを作成する必要があります。

  4. バッチに含める SQL ステートメントの SQLJ 実行可能節を組み込みます。

    これらの条項には、 ステップ2で作成した実行コンテキストを含める必要があります。

    SQLJ 実行可能節に入力パラメーターまたはホスト式が含まれている場合は、バッチの中にそのステートメントを複数回組み込んで、それぞれに入力パラメーターまたはホスト式の別々の値を適用することもできます。

    ステートメントが既存のバッチに追加されたかどうか、ステートメントが新しいバッチの最初のステートメントかどうか、ステートメントが実行されたのはバッチの中か外かを確認するには、ExecutionContext.getUpdateCount メソッドを呼び出します。 このメソッドは、以下のいずれかの値を返します。
    ExecutionContext.ADD_BATCH_COUNT
    ステートメントが既存のバッチに追加された場合は、この定数が返されます。
    ExecutionContext.NEW_BATCH_COUNT
    ステートメントが新しいバッチの最初のステートメントの場合は、この定数が返されます。
    ExecutionContext.EXEC_BATCH_COUNT
    ステートメントがバッチに含まれている状態でバッチが実行された場合は、この定数が返されます。
    Other integer
    この値は、ステートメントにより更新された行の数です。 ステートメントがバッチに追加されない状態で実行された場合は、この値が返されます。
  5. 明示的または暗黙的にバッチを実行します。
    • バッチを明示的に実行するには、ExecutionContext.executeBatch メソッドを呼び出します。

      executeBatch からは、バッチに含まれている各ステートメントによって更新された行の数を含んだ整数配列が返されます。 配列内のエレメントの順序は、最初に各ステートメントをバッチに追加した順序に対応します。 さまざまな入力パラメーター・セットまたはホスト式値を使用して複数回実行されるバッチ内の各ステートメントについて、そのステートメントの配列エレメントの順序は、ステートメントに値を追加した順序になります。

    • バッチは、以下の状況では暗黙的に実行されます。
      • 既にバッチに含まれているステートメントとの互換性のないバッチ可能ステートメントをプログラムに組み込んだ場合。 この場合、SQLJ は、既にバッチに含まれているステートメントを実行し、非互換ステートメントを組み込んだ新しいバッチを作成します。
      • バッチ可能でないステートメントをプログラムに組み込んだ場合。 この場合、SQLJ は、既にバッチに含まれているステートメントを実行します。 SQLJ は、バッチ可能でないステートメントも実行します。
      • ExecutionContext.setBatchLimit(n) メソッドを呼び出した後に、バッチにステートメントを追加すると、バッチ内のステートメントの数が N 以上になります。 n は、以下のいずれかの値になります。
        ExecutionContext.UNLIMITED_BATCH
        この定数を指定した場合は、バッチ可能ではあっても非互換のステートメント、またはバッチ可能でないステートメントを SQLJ が検出した場合にのみ、バッチが暗黙的に実行されます。 この値を設定することは、setBatchLimit を呼び出さないことと同じです。
        ExecutionContext.AUTO_BATCH
        この定数を指定した場合は、バッチ内のステートメントの数が SQLJ によって設定されている数に達した場合に、バッチが暗黙的に実行されます。
        Positive integer
        この数のステートメントがバッチに追加されると、SQLJ によってバッチが暗黙的に実行されます。 ただし、バッチ可能ではあっても非互換のステートメント、またはバッチ可能でないステートメントを SQLJ が検出した場合は、その数のステートメントが追加される前にバッチが実行される可能性があります。
      暗黙的に実行されたバッチで更新された行の数を確認するには、ExecutionContext.getBatchUpdateCounts メソッドを呼び出します。 getBatchUpdateCounts からは、バッチに含まれている各ステートメントによって更新された行の数を含んだ整数配列が返されます。 配列内の各エレメントの順序は、各ステートメントをバッチに追加した順序に対応します。 それぞれの配列エレメントには、以下のいずれかの値が入ります。
      -2
      この値は、SQL ステートメントは正常に実行されたが、更新された行数は判別できなかったことを示します。
      -3
      この値は SQL ステートメントが失敗したことを示します。
      Other integer
      この値は、ステートメントにより更新された行の数です。
  6. オプションとして、すべてのステートメントをバッチに追加した後に、バッチ処理を使用不可にすることもできます。

    そのためには、ExecutionContext.setBatching(false) メソッドを呼び出します。 バッチ処理を使用不可にしても、暗黙的または明示的にバッチを実行することは可能ですが、バッチにさらにステートメントを追加することができなくなります。 バッチが既に存在する場合に、バッチ互換ステートメントをバッチに追加する代わりにそのまま実行することが望ましい状況では、バッチ処理を使用不可にすると便利です。

    バッチを実行しないでクリアする場合は、ExecutionContext.cancel メソッドを呼び出します。

  7. バッチ実行が暗黙的であった場合は、最終的に明示的に executeBatch を実行し、すべてのステートメントが実行されたことを確認します。

以下に示すのは、UPDATE ステートメントのバッチ実行の例です。 選択されたステートメントの右にある番号は、前述のステップに対応しています。
#sql iterator GetMgr(String);       // Declare positioned iterator
…
{
  GetMgr deptiter;                  // Declare object of GetMgr class
  String mgrnum = null;             // Declare host variable for manager number
  int raise = 400;                  // Declare raise amount
  int currentSalary;                // Declare current salary
  String url, username, password;   // Declare url, user ID, password
  …
  TestContext c1 = new TestContext (url, username, password, false);  1 
  ExecutionContext ec = new ExecutionContext();                       2 
  ec.setBatching(true);                                               3 

  #sql [c1] deptiter = 
    {SELECT MGRNO FROM DEPARTMENT};
                                    // Assign the result table of the SELECT
                                    // to iterator object deptiter
  #sql {FETCH :deptiter INTO :mgrnum};
                                    // Retrieve the first manager number
  while (!deptiter.endFetch()) {    // Check whether the FETCH returned a row
    #sql [c1]
      {SELECT SALARY INTO :currentSalary FROM EMPLOYEE 
        WHERE EMPNO=:mgrnum};
    #sql [c1, ec]                                                     4 
      {UPDATE EMPLOYEE SET SALARY=:(currentSalary+raise) 
        WHERE EMPNO=:mgrnum};
    #sql {FETCH :deptiter INTO :mgrnum };
                                    // Fetch the next row
  }
  ec.executeBatch();                                                  5 
  ec.setBatching(false);                                              6 
  #sql [c1] {COMMIT};                                               
  deptiter.close();                 // Close the iterator
  c1.close();                       // Close the connection
}

以下に示すのは、INSERT ステートメントのバッチ実行の例です。 ATOMICTBL が次のように定義されているとします。

CREATE TABLE ATOMICTBL(
 INTCOL INTEGER NOT NULL UNIQUE, 
 CHARCOL VARCHAR(10))

また、この表には、2 および "val2" という値を持つ行が 1 つ既に含まれているものとします。 INTCOL の固有性制約のため、以下のコードを実行すると、バッチ中の 2 番目の INSERT ステートメントは失敗します。

ターゲットデータサーバーが Db2 for z/OS、 atomicMultiRowInsert を DB2BaseDataSource.YES に設定せずにこのアプリケーションをカスタマイズした場合、バッチ INSERT は非アトミックであるため、最初の値のセットがテーブルに挿入されます。 しかし、このアプリケーションが、atomicMultiRowInsert を DB2BaseDataSource.YES に設定してカスタマイズされているなら、バッチ INSERT はアトミックであり、最初の値セットは挿入されません。

選択されたステートメントの右にある番号は、前述のステップに対応しています。

 …
TestContext ctx = new TestContext (url, username, password, false);   1 
ctx.getExecutionContext().setBatching(true);                          2,3 
try {
  for (int i = 1; i<= 2; ++i) {
   if (i == 1) {
    intVar = 3;
    strVar = "val1";
   {
   if (i == 2) {
    intVar = 1;
    strVar = "val2";
   }
   #sql [ctx] {INSERT INTO ATOMICTBL values(:intVar, :strVar)};       4 
  }
  int[] counts = ctx.getExecutionContext().executeBatch();            5 
  for (int i = 0; i<counts.length; ++i) {
    System.out.println(" count[" + i + "]:" + counts[i]);
  }
}
catch (SQLException e) {
  System.out.println(" Exception Caught: " + e.getMessage());
   SQLException excp = null;
  if (e instanceof SQLException)
  {
    System.out.println(" SQLCode: " + ((SQLException)e).getErrorCode() + "
      Message: " + e.getMessage() );
    excp = ((SQLException)e).getNextException();
    while (  excp != null ) {
      System.out.println(" SQLCode: " + ((SQLException)excp).getErrorCode() +       
        " Message: " + excp.getMessage() );
      excp = excp.getNextException();
    }
  }
}