Oracle8i JDBC Developer's Guide and Reference Release 3 (8.1.7) Part Number A83724-01 |
Library | Service | Contents | Index

## Update Batching

You can reduce the number of round trips to the database, thereby improving application performance, by grouping multiple `UPDATE`, `DELETE`, or `INSERT` statements into a single "batch" and having the whole batch sent to the database and processed in one trip. This is referred to in this manual as update batching and in the Sun Microsystems JDBC 2.0 specification as batch updates.

This is especially useful with prepared statements, when you are repeating the same statement with different bind variables.

With Oracle8i release 8.1.6 and higher, Oracle JDBC supports two distinct models for update batching:

- the standard model, supported since Oracle8i release 8.1.6 and implementing the Sun Microsystems JDBC 2.0 Specification, which is referred to as standard update batching

- the Oracle-specific model, supported since release 8.1.5 and independent of the Sun Microsystems JDBC 2.0 Specification, which is referred to as Oracle update batching

  Note: It is important to be aware that you cannot mix theses models. In any single application, you can use the syntax of one model or the other, but not both. The Oracle JDBC driver will throw exceptions when you mix these syntaxes.

### Overview of Update Batching Models

This section compares and contrasts the general models and types of statements supported for standard update batching and Oracle update batching.

#### Oracle Model versus Standard Model

Oracle update batching uses a batch value that typically results in implicit processing of a batch. The batch value is the number of operations you want to batch (accumulate) for each trip to the database. As soon as that many operations have been added to the batch, the batch is executed. Note the following:

- You can set a default batch for the connection object, which applies to any prepared statement executed in that connection.

- For any individual prepared statement object, you can set a statement batch value that overrides the connection batch value.

- You can choose to explicitly execute a batch at any time, overriding both the connection batch value and the statement batch value.

Standard update batching is a manual, explicit model. There is no batch value. You manually add operations to the batch and then explicitly choose when to execute the batch.

Oracle update batching is a more efficient model because the driver knows ahead of time how many operations will be batched. In this sense, the Oracle model is more static and predictable. With the standard model, the driver has no way of knowing in advance how many operations will be batched. In this sense, the standard model is more dynamic in nature.

If you want to use update batching, here is how to choose between the two models:

- Use Oracle update batching if portability is not critical. This will probably result in the greatest performance improvement.

- Use standard update batching if portability is a higher priority than performance.

#### Types of Statements Supported

As implemented by Oracle, update batching is intended for use with prepared statements, when you are repeating the same statement with different bind variables. Be aware of the following:

- Oracle update batching supports only Oracle prepared statement objects. In an Oracle callable statement, both the connection default batch value and the statement batch value are overridden with a value of 1. In an Oracle generic statement, there is no statement batch value, and the connection default batch value is overridden with a value of 1.

  Note that because Oracle update batching is vendor-specific, you must actually use (or cast to) `OraclePreparedStatement` objects, not general `PreparedStatement` objects.

- To adhere to the JDBC 2.0 standard, Oracle's implementation of standard update batching supports callable statements and generic statements, as well as prepared statements. You can migrate standard update batching syntax into an Oracle JDBC application without difficulty.

- You can batch only `UPDATE`, `INSERT`, or `DELETE` operations. Executing a batch that includes an operation that attempts to return a result set will cause an exception.

  Note: The Oracle implementation of standard update batching does not implement true batching for generic statements and callable statements. Although Oracle JDBC supports the use of standard batching syntax for `Statement` and `CallableStatement` objects, you will see performance improvement for only `PreparedStatement` objects.

  Note that with standard update batching, you can use either standard `PreparedStatement`, `CallableStatement`, and `Statement` objects, or Oracle-specific `OraclePreparedStatement`, `OracleCallableStatement`, and `OracleStatement` objects.

### Oracle Update Batching

The Oracle update batching feature associates a batch value (limit) with each prepared statement object. With Oracle update batching, instead of the JDBC driver executing a prepared statement each time its `executeUpdate()` method is called, the driver adds the statement to a batch of accumulated execution requests. The driver will pass all the operations to the database for execution once the batch value is reached. For example, if the batch value is 10, then each batch of 10 operations will be sent to the database and processed in one trip.

A method in the `OracleConnection` class allows you to set a default batch value for the Oracle connection as a whole, and this batch value is relevant to any Oracle prepared statement in the connection. For any particular Oracle prepared statement, a method in the `OraclePreparedStatement` class allows you to set a statement batch value that overrides the connection batch value. You can also override both batch values by choosing to manually execute the pending batch.

Notes: Do not mix standard update batching syntax with Oracle update batching syntax in the same application. The JDBC driver will throw an exception when you mix these syntaxes. Disable auto-commit mode if you use either update batching model. In case an error occurs while you are executing a batch, this allows you the option of committing or rolling back the operations that executed successfully prior to the error.

#### Oracle Update Batching Characteristics and Limitations

Note the following limitations and implementation details regarding Oracle update batching:

- By default, there is no statement batch value, and the connection (default) batch value is 1.

- Batch values between 5 and 30 tend to be the most effective. Setting a very high value might even have a negative effect. It is worth trying different values to verify the effectiveness for your particular application.

- Regardless of the batch value in effect, if any of the bind variables of an Oracle prepared statement is (or becomes) a stream type, then the Oracle JDBC driver sets the batch value to 1 and sends any queued requests to the database for execution.

- The Oracle JDBC driver automatically executes the `sendBatch()` method of an Oracle prepared statement in any of the following circumstances: 1) the connection receives a `COMMIT` request, either as a result of invoking the `commit()` method or as a result of auto-commit mode; 2) the statement receives a `close()` request; or 3) the connection receives a `close()` request.

  Note: A connection `COMMIT` request, statement close, or connection close has no effect on a pending batch if you use standard update batching--only if you use Oracle update batching.

#### Setting the Connection Batch Value

You can specify a default batch value for any Oracle prepared statement in your Oracle connection. To do this, use the `setDefaultExecuteBatch()` method of the `OracleConnection` object. For example, the following code sets the default batch value to 20 for all prepared statement objects associated with the `conn` connection object:

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

Even though this sets the default batch value for all the prepared statements of the connection, you can override it by calling `setDefaultBatch()` on individual Oracle prepared statements.

The connection batch value will apply to statement objects created after this batch value was set.

Note that instead of calling `setDefaultExecuteBatch()`, you can set the `defaultBatchValue` Java property if you use a Java `Properties` object in establishing the connection. See "Specifying a Database URL and Properties Object".

#### Setting the Statement Batch Value

Use the following steps to set the statement batch value for a particular Oracle prepared statement. This will override any connection batch value set using the `setDefaultExecuteBatch()` method of the `OracleConnection` instance for the connection in which the statement executes.

1. Write your prepared statement and specify input values for the first row:

       PreparedStatement ps = conn.prepareStatement
                                     ("INSERT INTO dept VALUES (?,?,?)");
       ps.setInt (1,12);
       ps.setString (2,"Oracle");
       ps.setString (3,"USA");

2. Cast your prepared statement to an `OraclePreparedStatement` object, and apply the `setExecuteBatch()` method. In this example, the batch size of the statement is set to 2.

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

   If you wish, insert the `getExecuteBatch()` method at any point in the program to check the default batch value for the statement:

       System.out.println (" Statement Execute Batch Value " +
                          ((OraclePreparedStatement)ps).getExecuteBatch());

3. If you send an execute-update call to the database at this point, then no data will be sent to the database, and the call will return 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. If you enter a set of input values for a second row and an execute-update, then the number of batch calls to `executeUpdate()` will be equal to the batch value of 2. The data will be sent to the database, and both rows will be inserted in a single round trip.

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

#### Checking the Batch Value

To check the overall connection batch value of an Oracle connection instance, use the `OracleConnection` class `getDefaultExecuteBatch()` method:

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

To check the particular statement batch value of an Oracle prepared statement, use the `OraclePreparedStatement` class `getExecuteBatch()` method:

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

Note: If no statement batch value has been set, then `getExecuteBatch()` will return the connection batch value.

#### Overriding the Batch Value

If you want to execute accumulated operations before the batch value in effect is reached, then use the `sendBatch()` method of the `OraclePreparedStatement` object.

For this example, presume you set the connection batch value to 20. (This sets the default batch value for all prepared statement objects associated with the connection to 20.) You could accomplish this by casting your connection to an `OracleConnection` object and applying the `setDefaultExecuteBatch()` method for the connection, as follows:

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

Override the batch value as follows:

1. Write your prepared statement and specify input values for the first row as usual, then execute the statement:

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

   The batch is not executed at this point. The `ps.executeUpdate()` method returns "0".

2. If you enter a set of input values for a second operation and call `executeUpdate()` again, the data will still not be sent to the database, because the batch value in effect for the statement is the connection batch value: 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);

   Note that the value of `rows` in the `println` statement is "0".

3. If you apply the `sendBatch()` method at this point, then the two previously batched operations will be sent to the database in a single round trip. The `sendBatch()` method also returns the total number of updated rows. This property of `sendBatch()` is used by `println` to print the number of updated rows.

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

#### Committing the Changes in Oracle Batching

After you execute the batch, you must still commit the changes, presuming auto-commit is disabled as recommended.

Calling `commit()` on the connection object in Oracle batching not only commits operations in batches that have been executed, but also issues an implicit `sendBatch()` call to execute all pending batches. So `commit()` effectively commits changes for all operations that have been added to a batch.

#### Update Counts in Oracle Batching

In a non-batching situation, the `executeUpdate()` method of an `OraclePreparedStatement` object will return the number of database rows affected by the operation.

In an Oracle batching situation, this method returns the number of rows affected at the time the method is invoked, as follows:

- If an `executeUpdate()` call results in the operation being added to the batch, then the method returns a value of 0, because nothing was written to the database yet.

- If an `executeUpdate()` call results in the batch value being reached and the batch being executed, then the method will return the total number of rows affected by all operations in the batch.

Similarly, the `sendBatch()` method of an `OraclePreparedStatement` object returns the total number of rows affected by all operations in the batch.

#### Example: Oracle Update Batching

The following example illustrates how you use the Oracle update batching feature. It assumes you have imported the `oracle.jdbc.driver.*` classes.

    ...
    Connection conn =
      DriverManager.getConnection("jdbc:oracle:oci8:","scott","tiger");

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

For complete sample applications for Oracle update batching, including how to execute the batch both implicitly and explicitly, see "Oracle Update Batching with Implicit Execution--SetExecuteBatch.java" and "Oracle Update Batching with Explicit Execution--SendBatch.java".

Note: Updates deferred through batching can affect the results of other queries. In the following example, if the first query is deferred due to batching, then the second will return unexpected results:
    UPDATE emp SET name = "Sue" WHERE name = "Bob";
    SELECT name FROM emp WHERE name = "Sue";

### Standard Update Batching

With Oracle8i release 8.1.6 and higher, Oracle implements the standard update batching model according to the Sun Microsystems JDBC 2.0 Specification. Because it is a JDBC 2.0 feature, it is intended for use in a JDK 1.2.x environment. To use standard update batching in a JDK 1.1.x environment, you must cast to Oracle statement objects.

This model, unlike the Oracle update batching model, depends on explicitly adding statements to the batch using an `addBatch()` method and explicitly executing the batch using an `executeBatch()` method. (In the Oracle model, you invoke `executeUpdate()` as in a non-batching situation, but whether an operation is added to the batch or the whole batch is executed is typically determined implicitly, depending on whether a pre-determined batch value is reached.)

Notes: Do not mix standard update batching syntax with Oracle update batching syntax in the same application. The Oracle JDBC driver will throw exceptions when these syntaxes are mixed. Disable auto-commit mode if you use either update batching model. In case an error occurs while you are executing a batch, this allows you the option of committing or rolling back the operations that executed successfully prior to the error.

#### Limitations in the Oracle Implementation of Standard Batching

Note the following limitations and implementation details regarding Oracle's implementation of standard update batching:

- In Oracle JDBC applications, update batching is intended for use with prepared statements that are being executed repeatedly with different sets of bind values.

  The Oracle implementation of standard update batching does not implement true batching for generic statements and callable statements. Even though Oracle JDBC supports the use of standard batching syntax for `Statement` and `CallableStatement` objects, you are unlikely to see performance improvement.

- Oracle's implementation of standard update batching does not support stream types as bind values. (This is also true of Oracle update batching.) Any attempt to use stream types will result in an exception.

#### Adding Operations to the Batch

When any statement object is first created, its statement batch is empty. Use the standard `addBatch()` method to add an operation to the statement batch. This method is specified in the standard `java.sql.Statement`, `PreparedStatement`, and `CallableStatement` interfaces, which are implemented by classes `oracle.jdbc.driver.OracleStatement`, `OraclePreparedStatement`, and `OracleCallableStatement`, respectively.

For a `Statement` object (or `OracleStatement`), the `addBatch()` method takes a Java string with a SQL operation as input. For example (assume a `Connection` instance `conn`):

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

At this point, three operations are in the batch.

(Remember, however, that in the Oracle implementation of standard update batching, you will probably see no performance improvement in batching generic statements.)

For prepared statements, update batching is used to batch multiple executions of the same statement with different sets of bind parameters. For a `PreparedStatement` or `OraclePreparedStatement` object, the `addBatch()` method takes no input--it simply adds the operation to the batch using the bind parameters last set by the appropriate `setXXX()` methods. (This is also true for `CallableStatement` or `OracleCallableStatement` objects, but remember that in the Oracle implementation of standard update batching, you will probably see no performance improvement in batching callable statements.)

For example (again assuming a `Connection` instance `conn`):

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

At this point, two operations are in the batch.

Because a batch is associated with a single prepared statement object, you can batch only repeated executions of a single prepared statement, as in this example.

#### Executing the Batch

To execute the current batch of operations, use the `executeBatch()` method of the statement object. This method is specified in the standard `Statement` interface, which is extended by the standard `PreparedStatement` and `CallableStatement` interfaces.

Following is an example that repeats the prepared statement `addBatch()` calls shown previously and then executes the batch:

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

The `executeBatch()` method returns an `int` array, typically one element per batched operation, indicating success or failure in executing the batch and sometimes containing information about the number of rows affected. This is discussed in "Update Counts in the Oracle Implementation of Standard Batching".

Notes: After calling `addBatch()`, you must call either `executeBatch()` or `clearBatch()` before a call to `executeUpdate()`, otherwise there will be a SQL exception. When a batch is executed, operations are performed in the order in which they were batched. The statement batch is reset to empty once `executeBatch()` has returned. An `executeBatch()` call closes the statement object's current result set, if one exists.

#### Committing the Changes in the Oracle Implementation of Standard Batching

After you execute the batch, you must still commit the changes, presuming auto-commit is disabled as recommended.

Calling `commit()` commits non-batched operations and commits batched operations for statement batches that have been executed, but for the Oracle implementation of standard batching, has no effect on pending statement batches that have not been executed.

#### Clearing the Batch

To clear the current batch of operations instead of executing it, use the `clearBatch()` method of the statement object. This method is specified in the standard `Statement` interface, which is extended by the standard `PreparedStatement` and `CallableStatement` interfaces.

Following is an example that repeats the prepared statement `addBatch()` calls shown previously but then clears the batch under certain circumstances:

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

Notes: After calling `addBatch()`, you must call either `executeBatch()` or `clearBatch()` before a call to `executeUpdate()`, otherwise there will be a SQL exception. A `clearBatch()` call resets the statement batch to empty. Nothing is returned by the `clearBatch()` method.

#### Update Counts in the Oracle Implementation of Standard Batching

If a statement batch is executed successfully (no batch exception is thrown), then the integer array--or update counts array--returned by the statement `executeBatch()` call will always have one element for each operation in the batch. In the Oracle implementation of standard update batching, the values of the array elements are as follows:

- For a prepared statement batch, it is not possible to know the number of rows affected in the database by each individual statement in the batch. Therefore, all array elements have a value of -2. According to the JDBC 2.0 specification, a value of -2 indicates that the operation was successful but the number of rows affected is unknown.

- For a generic statement batch or callable statement batch, the array contains the actual update counts indicating the number of rows affected by each operation. The actual update counts can be provided because Oracle JDBC cannot use true batching for generic and callable statements in the Oracle implementation of standard update batching.

In your code, upon successful execution of a batch, you should be prepared to handle either -2's or true update counts in the array elements. For a successful batch execution, the array contains either all -2's or all positive integers.

Note: For information about possible values in the update counts array for an unsuccessful batch execution, see "Error Handling in the Oracle Implementation of Standard Batching".

#### Example: Standard Update Batching

This example combines the sample fragments in the previous sections, accomplishing the following steps:

- disabling auto-commit mode (which you should always do when using either update batching model)

- creating a prepared statement object

- adding operations to the batch associated with the prepared statement object

- executing the batch

- committing the operations from the batch

Assume a `Connection` instance `conn`:

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

You can process the update counts array to determine if the batch executed successfully. This is discussed in the next section ("Error Handling in the Oracle Implementation of Standard Batching").

For a complete sample application, see "Standard Update Batching--BatchUpdates.java".

#### Error Handling in the Oracle Implementation of Standard Batching

If any one of the batched operations fails to complete successfully (or attempts to return a result set) during an `executeBatch()` call, then execution stops and a `java.sql.BatchUpdateException` is generated (a subclass of `java.sql.SQLException`).

After a batch exception, the update counts array can be retrieved using the `getUpdateCounts()` method of the `BatchUpdateException` object. This returns an `int` array of update counts, just as the `executeBatch()` method does. In the Oracle implementation of standard update batching, contents of the update counts array are as follows after a batch exception:

- For a prepared statement batch, it is not possible to know which operation failed. The array has one element for each operation in the batch, and each element has a value of -3. According to the JDBC 2.0 specification, a value of -3 indicates that an operation did not complete successfully. In this case, it was presumably just one operation that actually failed, but because the JDBC driver does not know which operation that was, it labels all the batched operations as failures.

  You should always perform a `ROLLBACK` operation in this situation.

- For a generic statement batch or callable statement batch, the update counts array is only a partial array containing the actual update counts up to the point of the error. The actual update counts can be provided because Oracle JDBC cannot use true batching for generic and callable statements in the Oracle implementation of standard update batching.

  For example, if there were 20 operations in the batch, the first 13 succeeded, and the 14th generated an exception, then the update counts array will have 13 elements, containing actual update counts of the successful operations.

  You can either commit or roll back the successful operations in this situation, as you prefer.

In your code, upon failed execution of a batch, you should be prepared to handle either -3's or true update counts in the array elements when an exception occurs. For a failed batch execution, you will have either a full array of -3's or a partial array of positive integers.

#### Intermixing Batched Statements and Non-Batched Statements

You cannot call `executeUpdate()` for regular, non-batched execution of an operation if the statement object has a pending batch of operations (essentially, if the batch associated with that statement object is non-empty).

You can, however, intermix batched operations and non-batched operations in a single statement object if you execute non-batched operations either prior to adding any operations to the statement batch or after executing the batch. Essentially, you can call `executeUpdate()` for a statement object only when its update batch is empty. If the batch is non-empty, then an exception will be generated.

For example, it is legal to have a sequence such as the following:

    ...
    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
    ...

Intermixing non-batched operations on one statement object and batched operations on another statement object within your code is permissible. Different statement objects are independent of each other with regards to update batching operations. A `COMMIT` request will affect all non-batched operations and all successful operations in executed batches, but will not affect any pending batches.

| Copyright © 1996-2000, Oracle Corporation. All Rights Reserved. |
Library | Service | Contents | Index