DB2 Version 9.7 for Linux, UNIX, and Windows

Making batch updates in SQLJ applications

The IBM® Data Server Driver for JDBC and SQLJ supports batch updates in SQLJ. With batch updates, instead of updating rows of a table one at a time, you can direct SQLJ to execute a group of updates at the same time.

You can include the following types of statements in a batch update:
  • Searched INSERT, UPDATE, or DELETE, or MERGE statements
  • CREATE, ALTER, DROP, GRANT, or REVOKE statements
  • CALL statements with input parameters only
Unlike JDBC, SQLJ allows heterogeneous batches that contain statements with input parameters or host expressions. You can therefore combine any of the following items in an SQLJ batch:
  • Instances of the same statement
  • Different statements
  • Statements with different numbers of input parameters or host expressions
  • Statements with different data types for input parameters or host expressions
  • Statements with no input parameters or host expressions

For all cases except homogeneous batches of INSERT statements, when an error occurs during execution of a statement in a batch, the remaining statements are executed, and a BatchUpdateException is thrown after all the statements in the batch have executed.

For homogeneous batches of INSERT statements, the behavior is as follows:
  • If you set atomicMultiRowInsert to DB2BaseDataSource.YES (1) when you run db2sqljcustomize, and the target data server is DB2® for z/OS®, when an error occurs during execution of an INSERT statement in a batch, the remaining statements are not executed, and a BatchUpdateException is thrown.
  • If you do not set atomicMultiRowInsert to DB2BaseDataSource.YES (1) when you run db2sqljcustomize, or the target data server is not DB2 for z/OS, when an error occurs during execution of an INSERT statement in a batch, the remaining statements are executed, and a BatchUpdateException is thrown after all the statements in the batch have executed.

To obtain information about warnings, use the ExecutionContext.getWarnings method on the ExecutionContext that you used to submit statements to be batched. You can then retrieve an error description, SQLSTATE, and error code for each SQLWarning object.

When a batch is executed implicitly because the program contains a statement that cannot be added to the batch, the batch is executed before the new statement is processed. If an error occurs during execution of the batch, the statement that caused the batch to execute does not execute.

The basic steps for creating, executing, and deleting a batch of statements are:

  1. Disable AutoCommit for the connection.

    Do this so that you can control whether to commit changes to already-executed statements when an error occurs during batch execution.

  2. Acquire an execution context.

    All statements that execute in a batch must use this execution context.

  3. Invoke the ExecutionContext.setBatching(true) method to create a batch.

    Subsequent batchable statements that are associated with the execution context that you created in step 2 are added to the batch for later execution.

    If you want to batch sets of statements that are not batch compatible in parallel, you need to create an execution context for each set of batch compatible statements.

  4. Include SQLJ executable clauses for SQL statements that you want to batch.

    These clauses must include the execution context that you created in step 2.

    If an SQLJ executable clause has input parameters or host expressions, you can include the statement in the batch multiple times with different values for the input parameters or host expressions.

    To determine whether a statement was added to an existing batch, was the first statement in a new batch, or was executed inside or outside a batch, invoke the ExecutionContext.getUpdateCount method. This method returns one of the following values:
    ExecutionContext.ADD_BATCH_COUNT
    This is a constant that is returned if the statement was added to an existing batch.
    ExecutionContext.NEW_BATCH_COUNT
    This is a constant that is returned if the statement was the first statement in a new batch.
    ExecutionContext.EXEC_BATCH_COUNT
    This is a constant that is returned if the statement was part of a batch, and the batch was executed.
    Other integer
    This value is the number of rows that were updated by the statement. This value is returned if the statement was executed rather than added to a batch.
  5. Execute the batch explicitly or implicitly.
    • Invoke the ExecutionContext.executeBatch method to execute the batch explicitly.

      executeBatch returns an integer array that contains the number of rows that were updated by each statement in the batch. The order of the elements in the array corresponds to the order in which you added statements to the batch.

    • Alternatively, a batch executes implicitly under the following circumstances:
      • You include a batchable statement in your program that is not compatible with statements that are already in the batch. In this case, SQLJ executes the statements that are already in the batch and creates a new batch that includes the incompatible statement.
      • You include a statement in your program that is not batchable. In this case, SQLJ executes the statements that are already in the batch. SQLJ also executes the statement that is not batchable.
      • After you invoke the ExecutionContext.setBatchLimit(n) method, you add a statement to the batch that brings the number of statements in the batch to n or greater. n can have one of the following values:
        ExecutionContext.UNLIMITED_BATCH
        This constant indicates that implicit execution occurs only when SQLJ encounters a statement that is batchable but incompatible, or not batchable. Setting this value is the same as not invoking setBatchLimit.
        ExecutionContext.AUTO_BATCH
        This constant indicates that implicit execution occurs when the number of statements in the batch reaches a number that is set by SQLJ.
        Positive integer
        When this number of statements have been added to the batch, SQLJ executes the batch implicitly. However, the batch might be executed before this many statements have been added if SQLJ encounters a statement that is batchable but incompatible, or not batchable.
      To determine the number of rows that were updated by a batch that was executed implicitly, invoke the ExecutionContext.getBatchUpdateCounts method. getBatchUpdateCounts returns an integer array that contains the number of rows that were updated by each statement in the batch. The order of the elements in the array corresponds to the order in which you added statements to the batch. Each array element can be one of the following values:
      -2
      This value indicates that the SQL statement executed successfully, but the number of rows that were updated could not be determined.
      -3
      This value indicates that the SQL statement failed.
      Other integer
      This value is the number of rows that were updated by the statement.
  6. Optionally, when all statements have been added to the batch, disable batching.

    Do this by invoking the ExecutionContext.setBatching(false) method. When you disable batching, you can still execute the batch implicitly or explicitly, but no more statements are added to the batch. Disabling batching is useful when a batch already exists, and you want to execute a batch compatible statement, rather than adding it to the batch.

    If you want to clear a batch without executing it, invoke the ExecutionContext.cancel method.

  7. If batch execution was implicit, perform a final, explicit executeBatch to ensure that all statements have been executed.

Example

The following example demonstrates batching of UPDATE statements. The numbers to the right of selected statements correspond to the previously described steps.
#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
}

The following example demonstrates batching of INSERT statements. Suppose that ATOMICTBL is defined like this:

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

Also suppose that the table already has a row with the values 2 and "val2". Because of the uniqueness constraint on INTCOL, when the following code is executed, the second INSERT statement in the batch fails.

If the target data server is DB2 for z/OS, and this application is customized without atomicMultiRowInsert set to DB2BaseDataSource.YES, the batch INSERT is non-atomic, so the first set of values is inserted in the table. However, if the application is customized with atomicMultiRowInsert set to DB2BaseDataSource.YES, the batch INSERT is atomic, so the first set of values is not inserted.

The numbers to the right of selected statements correspond to the previously described steps.

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