Skip Headers
Oracle® TimesTen In-Memory Database Java Developer's Guide
Release 11.2.1

Part Number E13068-02
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

2 Working with TimesTen Data Stores

This chapter describes the basic procedures for writing a Java application to access data in a TimesTen data store. Before attempting to write a TimesTen application, be sure you have completed the following prerequisite tasks:

Prerequisite task What you do
Create a TimesTen data store. Follow the procedures described in "Creating TimesTen Data Stores" in Oracle TimesTen In-Memory Database Operations Guide.
Configure the Java environment. Follow the procedures described in "Setting the Java environment variables".
Compile and execute the TimesTen Java demos. Follow the procedures described in "About the TimesTen Java demos".

After you have successfully executed the TimesTen Java demos, your development environment is set up correctly and ready for you to create applications that accesses a TimesTen data store.

Topics in this chapter are:

Key JDBC classes and interfaces

This section discusses important JDBC packages, classes, and interfaces, both standard and TimesTen-specific. The following topics are covered:

For reference information on standard JDBC, see the following:

http://java.sun.com/j2se/1.5.0/docs/api/index.html

For reference information on TimesTen JDBC extensions, refer to Oracle TimesTen In-Memory Database JDBC Extensions Java API Reference.

Package imports

You must import the standard JDBC package in any Java program that use JDBC:

import java.sql.*;

If you are going to use data sources or pooled connections, you must also import the standard extended JDBC package:

import javax.sql.*;

You must import the TimesTen JDBC package:

import com.timesten.jdbc.*;

To use XA data sources for JTA, you must also import the following TimesTen package:

import com.timesten.jdbc.xa.*;

Support for interfaces in the java.sql package

TimesTen supports the java.sql interfaces shown in Table 2-1.

Table 2-1 Supported java.sql interfaces

Interface in java.sql Remarks on TimesTen support

CallableStatement

  • You must pass parameters to CallableStatement by position, not by name.

  • You cannot use SQL escape syntax.

  • There is no support for CLOB, BLOB, Array, Struct, or Ref.

  • There is no support for Calendar for setDate(), getDate(), setTime(), or getTime().

Connection

  • There is no support for savepoints.

DatabaseMetaData

  • No restrictions.

ParameterMetaData

  • The JDBC driver cannot determine whether a column is nullable and always returns parameterNullableUnknown from calls to isNullable().

  • The getScale() method returns 1 for VARCHAR, NVARCHAR and VARBINARY data types if they are INLINE. (Scale is of no significance to these data types.)

PreparedStatement

  • There is no support for getMetaData() in PreparedStatement.

  • There is no support for CLOB, BLOB, Array, Struct, or Ref.

  • There is no support for Calendar for setDate(), getDate(), setTime(), or getTime().

Statement

  • No restrictions.

  • See "Managing cache groups" for special TimesTen functionality of the getUpdateCount() method with cache groups.

ResultSet

  • There is support for getMetaData() in ResultSet.

  • You cannot have multiple open ResultSet objects per statement.

  • You cannot specify the holdability of a result set, so a cursor cannot remain open after it has been committed.

  • There is no support for scrollable or updatable result sets.

  • There is no support for CLOB, BLOB, Array, Struct, or Ref.

  • There is no support for Calendar for setDate(), getDate(), setTime(), or getTime().

  • See "Working with TimesTen result sets: hints and restrictions".

ResultSetMetaData

  • The getPrecision() method returns 0 for undefined precision.

  • The getScale() method returns -127 for undefined scale.


Support for classes in the java.sql package

TimesTen supports the following java.sql classes:

  • Date

  • DriverManager

  • DriverPropertyInfo

  • Time

  • Timestamp

  • Types

  • DataTruncation

  • SQLException

  • SQLWarning

Support for interfaces in the javax.sql package

TimesTen supports the following javax.sql interfaces:

  • DataSource is implemented by TimesTenDataSource.

  • PooledConnection is implemented by ObservableConnection.

  • ConnectionPoolDataSource is implemented by ObservableConnectionDS.

  • XADataSource is implemented by TimesTenXADataSource (in package com.timesten.jdbc.xa).

Important:

The TimesTen JDBC driver itself does not implement a database connection pool. The ObservableConnection and ObservableConnectionDS classes merely implement standard Java EE interfaces, facilitating the creation and management of database connection pools according to the Java EE standard.

TimesTen JDBC extensions

For most scenarios, you can use standard JDBC functionality as supported by TimesTen.

TimesTen also provides the following extensions in the com.timesten.jdbc package for TimesTen-specific features.

Table 2-2 TimesTen JDBC extensions

Interface Extends Remarks

TimesTenConnection

Connection

Provides capabilities such as prefetching rows to improve performance, and listening to events for automatic client failover.

See "Fetching multiple rows of data" and "General Client Failover Features".

TimesTenStatement

Statement

Provides capabilities for specifying a query threshold.

See "Setting a threshold value for SQL statements".

TimesTenPreparedStatement

PreparedStatement

Supports DML returning.

See "Working with DML returning (RETURNING INTO clause)".

TimesTenCallableStatement

CallableStatement

Supports PL/SQL REF CURSORs.

See "Working with PL/SQL REF CURSORs".


Additional TimesTen classes and interfaces

In addition to implementations discussed previously, TimesTen provides the following classes and interfaces in the com.timesten.jdbc package. Features supported by these classes and interfaces are discussed later in this chapter.

Additional TimesTen Interfaces

  • Use TimesTenTypes for TimesTen type extensions (for REF CURSORs).

  • Use ClientFailoverEventListener (and also the ClientFailoverEvent class below) for automatic client failover features. See "JDBC support for automatic client failover".

  • Use TimesTenVendorCode for vendor codes used in SQL exceptions.

Additional TimesTen Classes

  • Use ClientFailoverEvent (and also the ClientFailoverEventListener interface above) for automatic client failover features.

Connecting to a TimesTen data store

The type of DSN you create depends on whether your application connects directly to the data store or connects by a client. If you intend to connect directly to the data store, create a DSN as described in "Creating a DSN on UNIX" or "Creating a DSN on Windows" in Oracle TimesTen In-Memory Database Operations Guide. If you intend to create a client connection to the data store, create a DSN as described in "Creating and configuring Client DSNs on Windows" or "Creating and configuring Client DSNs on UNIX" in Oracle TimesTen In-Memory Database Operations Guide.

After you have created a DSN, the application can connect to the data store. This section describes how to create a JDBC connection to a data store using either the JDBC direct driver or the JDBC client driver.

The operations described in this section are based on the level1 demo. Refer to "About the TimesTen Java demos".

This following topics are covered here:

Load the TimesTen driver

The TimesTen JDBC driver must be loaded before it is available for making connections with a TimesTen data store. The TimesTen JDBC driver is:

com.timesten.jdbc.TimesTenDriver

If you are using the DriverManager interface to connect to TimesTen, call the Class.forName() method to load the TimesTen JDBC driver. This method creates an instance of the TimesTen driver and registers it with the driver manager. If you are using the TimesTenDataSource interface, you are not required to call Class.forName().

To identify and load the TimesTen driver:

Class.forName("com.timesten.jdbc.TimesTenDriver");

Note:

If the TimesTen JDBC driver is not loaded, TimesTen returns an error when the application attempts to connect to a TimesTen data store.

Create a connection URL for the data store and specify connection attributes

To create a JDBC connection, you must specify a TimesTen connection URL for the data store. The format of a TimesTen connection URL is as follows:

jdbc:timesten:{direct|client}:dsn=DSNname;[DSNattributes;]

The default is direct.

For example, the following creates a direct connection to the sample data store:

String URL = "jdbc:timesten:direct:dsn=sampledb_1121";

You can programmatically set or override the connection attributes in the DSN description by specifying attributes in the connection URL.

Refer to "Connection attributes for Data Manager DSNs" in Oracle TimesTen In-Memory Database Operations Guide for introductory information about connection attributes. General connection attributes require no special privilege. First connection attributes are set when the database is first loaded, and persist for all connections. Only the instance administrator can load a database with changes to first connection attribute settings. Refer to "Data Store Attributes" in Oracle TimesTen In-Memory Database Reference for specific information about any particular connection attribute, including required privilege.

For example, to set the LockLevel DSN general connection attribute to 1, create a URL as follows:

String URL = "jdbc:timesten:direct:dsn=sampledb_1121;LockLevel=1";

Connect to the data store

After you have defined a URL, you can use the getConnection() method of either DriverManager or TimesTenDataSource to connect to the TimesTen data store.

If you use the DriverManager.getConnection() method, specify the driver URL to connect to the data store.

import java.sql.*;
...
Connection conn = DriverManager.getConnection(URL);

To use the TimesTenDataSource method getConnection(), first create a data source. Then use the TimesTenDataSource method setUrl() to set the URL and getConnection() to connect:

import com.timesten.jdbc.TimesTenDataSource;
import java.sql.*;
...
TimesTenDataSource ds = new TimesTenDataSource();
ds.setUrl(URL);
Connection conn = ds.getConnection();

Either method returns a Connection object (conn in this example) that you can use as a handle to the data store. See the level1 demo for an example on how to use the DriverManager method getConnection(), and the level2 and level3 demos for examples of using the TimesTenDataSource method getConnection(). Refer to "About the TimesTen Java demos".

Note:

If the TimesTen JDBC driver is not loaded, TimesTen returns an error when the application attempts to connect to a TimesTen data store. See "Load the TimesTen driver".

Disconnect from the data store

When you are finished accessing the TimesTen data store, call the Connection method close() to close the connection to the data store.

If an error has occurred, you may want to roll back the transaction before disconnecting from the data store. See "Handling non-fatal errors" and "Rolling back failed transactions" for more information.

Opening and closing a direct driver connection

Example 2-1 shows the general framework for an application that uses the DriverManager class to create a direct driver connection to the sample data store; execute some SQL, and then close the connection. See the level1 demo for a working example. (See "About the TimesTen Java demos" regarding the demos.)

Example 2-1 Connecting, executing SQL, and disconnecting

String URL = "jdbc:timesten:dsn=sampledb_1121";
Connection conn = null;

try {
     Class.forName("com.timesten.jdbc.TimesTenDriver");
} catch (ClassNotFoundException ex) {
      // See "Handling errors"
}

try {
    // Open a connection to TimesTen
    conn = DriverManager.getConnection(URL);

    // Report any SQLWarnings on the connection
    // See "Reporting errors and warnings"

    // Do SQL operations
    // See "Managing TimesTen data"

// Close the connection to TimesTen
   conn.close();

// Handle any errors
} catch (SQLException ex) {
    // See "Handling errors"
}

Access control for connections

Privilege to connect to a TimesTen data store must be explicitly granted to every user other than the instance administrator, through the CREATE SESSION privilege. This is a system privilege so must be granted by an administrator to the user, either directly or through the PUBLIC role. Refer to "Managing Access Control" in Oracle TimesTen In-Memory Database Operations Guide for additional information and examples.

Managing TimesTen data

This section provides detailed information on working with data in a TimesTen data store. It includes the following topics:

Executing simple SQL statements

"Working with Data in a TimesTen Data Store" in Oracle TimesTen In-Memory Database Operations Guide describes how to use SQL to manage data in a TimesTen data store. This section describes how to use the createStatement() method of a Connection instance, and the executeUpdate() or executeQuery() method of a Statement instance, to execute a SQL statement within a Java application.

Unless statements are prepared in advance, use the execution methods of a Statement object, such as execute(), executeUpdate() or executeQuery(), depending on the nature of the SQL statement and any returned result set.

For SQL statements that are prepared in advance, use the same execution methods of a PreparedStatement object.

The execute() method returns true if there is a result set (for example, on a SELECT) or false if there is no result set (for example, on an INSERT, UPDATE, or DELETE). The executeUpdate() method returns the number of rows affected. For example, when executing an INSERT statement, the executeUpdate() method returns the number of rows inserted. The executeQuery() method returns a result set, so it should only be called when a result set is expected (for example, when executing a SELECT).

Notes:

Example 2-2 Executing an update

This example uses the executeUpdate() method on the Statement object to execute an INSERT into the customer table in the current schema. The connection must also be opened, which is not shown. See "Connect to the data store".

Connection conn;
Statement stmt;
...
// [Code to open connection. See "Connect to the data store"...] 
...
try {
    stmt = conn.createStatement();
    int numRows = stmt.executeUpdate("insert into customer values" 
                  + "(40, 'West', 'Big Dish', '123 Signal St.')");
}
catch (SQLException ex) {
    ...
}

Example 2-3 Executing a query

This example uses an executeQuery() call on the Statement object to execute a SELECT on the customer table in the current schema and display the returned java.sql.ResultSet instance:

Statement stmt;
. . . . . .
try {
  ResultSet rs = stmt.executeQuery("select cust_num, region, " +
                      "name, address from customer");
  System.out.println("Fetching result set...");
  while (rs.next()) {
    System.out.println("\n Customer number: " + rs.getInt(1));
    System.out.println(" Region: " + rs.getString(2));
    System.out.println(" Name: " + rs.getString(3));
    System.out.println(" Address: " + rs.getString(4));
    }
  } 
catch (SQLException ex) {
  ex.printStackTrace();
}

Working with TimesTen result sets: hints and restrictions

You use ResultSet objects to process query results. In addition, some methods and built-in procedures return TimesTen data in the form of a ResultSet object. This section describes what you should know when using ResultSet objects from TimesTen.

  • TimesTen does not support multiple open ResultSet objects per statement. TimesTen cannot return multiple ResultSet objects from a single Statement object without first closing the current result set.

  • TimesTen does not support holdable cursors. You cannot specify the holdability of a result set, essentially whether a cursor can remain open after it has been committed.

  • ResultSet objects are not scrollable or updatable, so you cannot specify ResultSet.TYPE_SCROLL_SENSITIVE or ResultSet.CONCUR_UPDATABLE.

  • Use the ResultSet method close() to close a result set as soon as you are done with it.

  • Calling the ResultSet method getString() is more costly with regard to performance if the underlying data type is not a string. Because Java strings are immutable, getString() must allocate space for a new string each time it is called. This makes getString() one of the more costly calls in JDBC. Do not use getString() to retrieve primitive numeric types, like byte or int, unless it is absolutely necessary. For example, it is much faster to call getInt() on an integer column.

    In addition, it is generally true for dates and timestamps that ResultSet native methods getDate() and getTimestamp() will have better performance than getString().

  • Application performance is influenced by the choice of getXXX() calls as well as by any required data transformations after invocation.

  • JDBC ignores the setting for the ConnectionCharacterSet attribute. It returns data in UTF-16 encoding.

Preparing SQL statements and setting input parameters

SQL statements that are to be executed more than once should be prepared in advance by calling the Connection method prepareStatement(). For maximum performance, prepare parameterized statements.

Notes:

  • It is generally true for time, dates, and timestamps that PreparedStatement native methods setTime(), setDate() and setTimestamp() will have better performance than setString().

  • Application performance is influenced by the choice of setXXX() calls as well as by any required data transformations before invocation.

  • Access control privileges are checked both when SQL is prepared and when it is executed in the database. Refer to "Considering TimesTen features for access control" for related information.

Example 2-4 Executing a query on a prepared statement

This example shows the basics of an executeQuery() call on a PreparedStatement object. It executes a prepared SELECT statement and displays the returned result set.

PreparedStatement pSel = conn.prepareStatement("select cust_num, " +
                         "region, name, address " +
                         "from customer" +
                         "where region = ?");
pSel.setInt(1,1);

try {
  ResultSet rs = pSel.executeQuery();

  while (rs.next()) {
    System.out.println("\n Customer number: " + rs.getInt(1));
    System.out.println(" Region: " + rs.getString(2));
    System.out.println(" Name: " + rs.getString(3));
    System.out.println(" Address: " + rs.getString(4));
  }
} 
catch (SQLException ex) {
   ex.printStackTrace();
}

Example 2-5 Using statements with parameters (single connection)

This example shows how a single parameterized statement can be substituted in place of four separate statements.

Rather than execute a similar INSERT statement with different values:

Statement.execute("insert into t1 values (1, 2)");
Statement.execute("insert into t1 values (3, 4)");
Statement.execute("insert into t1 values (5, 6)");
Statement.execute("insert into t1 values (7, 8)");

It is much more efficient to prepare a single parameterized INSERT statement and use PreparedStatement methods setXXX() to set the row values before each execute:

PreparedStatement pIns =
        conn.PreparedStatement("insert into t1 values (?,?)");

pIns.setInt(1, 1);
pIns.setInt(2, 2);
pIns.executeUpdate();

pIns.setInt(1, 3);
pIns.setInt(2, 4);
pIns.executeUpdate();

pIns.setInt(1, 5);
pIns.setInt(2, 6);
pIns.executeUpdate();

pIns.setInt(1, 7);
pIns.setInt(2, 8);
pIns.executeUpdate();

conn.commit();
pIns.close();

TimesTen shares prepared statements automatically after they have been committed. For example, if two or more separate connections to the data store each prepare the same statement, then the second, third, ... , nth prepare returns very quickly because TimesTen remembers the first prepared statement.

Example 2-6 Using statements with parameters (multiple connections)

This example, prepares three identical parameterized INSERT statements for three separate connections. The first prepared INSERT for connection conn1 is shared with the conn2 and conn3 connections and speeds up the prepare operations for pIns2 and pIns3:

Connection conn1;
Connection conn2;
Connection conn3;
.....
PreparedStatement pIns1 = conn1.prepareStatement
                  ("insert into t1 values (?,?)");

PreparedStatement pIns2 = conn2.prepareStatement
                  ("insert into t1 values (?,?)");

PreparedStatement pIns3 = conn3.prepareStatement
                  ("insert into t1 values (?,?)");

Note:

All tuning options, such as join ordering, indexes and locks, must match for the statement to be shared. Also, if the prepared statement references a temp table, it is only shared within a single connection.

Binding duplicate parameters

This section discusses considerations in binding duplicate parameters in either SQL statements or PL/SQL.

Binding duplicate parameters in SQL statements

TimesTen supports either of two modes for binding duplicate parameters in a SQL statement:

  • Oracle mode, where multiple instances of the same parameter name are considered to be distinct parameters.

  • Traditional TimesTen mode, as in earlier releases, where multiple instances of the same parameter name are considered to be multiple occurrences of the same parameter.

You can choose the desired mode through the DuplicateBindMode general connection attribute. DuplicateBindMode=0 (the default) is for Oracle mode, and DuplicateBindMode=1 is for TimesTen mode. Because this is a general connection attribute, different concurrent connections to the same database can use different values. Refer to "DuplicateBindMode" in Oracle TimesTen In-Memory Database Reference for additional information about this attribute.

The rest of this section provides details for each mode, considering the following query:

SELECT * FROM employees
  WHERE employee_id < :a AND manager_id > :a AND salary < :b;

Note:

This discussion applies only to SQL statements issued directly from JDBC (not through PL/SQL, for example).
Oracle mode for duplicate parameters

In Oracle mode, multiple instances of the same parameter name in a SQL statement are considered to be different parameters. When parameter position numbers are assigned, a number is given to each parameter occurrence without regard to name duplication. The application must, at a minimum, bind a value for the first occurrence of each parameter name. For any subsequent occurrence of a given parameter name, the application has the following choices:

  • It can bind a different value for the occurrence.

  • It can leave the parameter occurrence unbound, in which case it takes the same value as the first occurrence.

In either case, each occurrence still has a distinct parameter position number.

To use a different value for the second occurrence of a in the SQL statement above:

pstmt.setXXX(1, ...); /* first occurrence of :a */
pstmt.setXXX(2, ...); /* second occurrence of :a */
pstmt.setXXX(3, ...); /* occurrence of :b */

To use the same value for both occurrences of a:

pstmt.setXXX(1, ...); /* both occurrences of :a */
pstmt.setXXX(3, ...); /* occurrence of :b */

Parameter b is considered to be in position 3 regardless.

TimesTen mode for duplicate parameters

In TimesTen mode, SQL statements containing duplicate parameters are parsed such that only distinct parameter names are considered as separate parameters. The application binds a value only for each unique parameter, and no unique parameter can be left unbound.

Binding is based on the position of the first occurrence of a parameter name. Subsequent occurrences of the parameter name are bound to the same value, and are not given parameter position numbers.

For the SQL statement above, the two occurrences of a are considered to be a single parameter, so cannot be bound separately:

pstmt.setXXX(1, ...); /* both occurrences of :a */
pstmt.setXXX(2, ...); /* occurrence of :b */

Note that in TimesTen mode, parameter b is considered to be in position 2, not position 3.

Binding duplicate parameters in PL/SQL

The preceding discussion does not apply within PL/SQL. Instead, PL/SQL semantics apply, whereby you bind a value for each unique parameter. An application calling the following block, for example, would bind only one parameter, corresponding to :a.

DECLARE
   x NUMBER;
   y NUMBER;
BEGIN
   x:=:a;
   y:=:a;
END;

An application calling the following block would also bind only one parameter:

BEGIN
   INSERT INTO tab1 VALUES(:a, :a);
END

An application calling the following block would bind two parameters, with :a as parameter #1 and :b as parameter #2. The second parameter in each INSERT statement would take the same value as the first parameter in the first INSERT statement:

BEGIN
   INSERT INTO tab1 VALUES(:a, :a);
   INSERT INTO tab1 VALUES(:b, :a);
END

Putting it all together: preparing and executing SQL statements

This example prepares INSERT and SELECT statements, executes the INSERT twice, executes the SELECT, and prints the returned result set. The connection must also be opened, which is not shown here but is shown in "Connect to the data store". For a working example, see the level1 demo. (Refer to "About the TimesTen Java demos" regarding the demos.)

Example 2-7 Full example: preparing and executing

Connection conn;
...
// [Code to open connection. See "Connect to the data store"...]
...

// Disable auto-commit
conn.setAutoCommit(false);

    // Report any SQLWarnings on the connection
    // See "Reporting errors and warnings"

// Prepare a parameterized INSERT and a SELECT Statement
PreparedStatement pIns = conn.prepareStatement("insert into customer values (?,?,?,?)");

PreparedStatement pSel = conn.prepareStatement
    ("select cust_num, region, name, " +
    "address from customer");

// Data for first INSERT statement
pIns.setInt(1, 100);
pIns.setString(2, "N");
pIns.setString(3, "Fiberifics");
pIns.setString(4, "123 any street");

// Execute the INSERT statement
pIns.executeUpdate();

// Data for second INSERT statement
pIns.setInt(1, 101);
pIns.setString(2, "N");
pIns.setString(3, "Natural Foods Co.");
pIns.setString(4, "5150 Johnson Rd");

// Execute the INSERT statement
pIns.executeUpdate();

// Commit the inserts
conn.commit();

// Done with INSERTs, so close the prepared statement
pIns.close();

// Report any SQLWarnings on the connection. 
// See "Reporting errors and warnings".
reportSQLWarnings(conn.getWarnings());

// Execute the prepared SELECT statement
ResultSet rs = pSel.executeQuery();

System.out.println("Fetching result set...");
while (rs.next()) {
    System.out.println("\n Customer number: " + rs.getInt(1));
    System.out.println(" Region: " + rs.getString(2));
    System.out.println(" Name: " + rs.getString(3));
    System.out.println(" Address: " + rs.getString(4));
}

// Close the result set.
rs.close();

// Commit the select - yes selects must be committed too
conn.commit();

// Close the select statement - we're done with it
pSel.close();

For additional information about using result sets, see "Working with TimesTen result sets: hints and restrictions".

Calling TimesTen built-in procedures

"Built-In Procedures" in Oracle TimesTen In-Memory Database Reference describes the TimesTen built-in procedures that extend standard ODBC functionality, and any privileges they require. You can execute a TimesTen built-in procedure through functionality of the CallableStatement interface.

Execute a built-in procedure as follows:

CallableStatement.execute("{ Call procedure }")

To prepare and execute a built-in procedure, use the format:

CallableStatement cStmt;
cStmt = conn.prepareCall("{ Call procedure }");
cStmt.execute();

For built-in procedures that return results, you can use the getXXX() methods of the ResultSet interface to retrieve the data, as demonstrated in Example 2-9.

Note:

See "Working with TimesTen result sets: hints and restrictions" for details about what you should know when working with result sets generated by TimesTen.

Example 2-8 Executing a ttCkpt call

This example calls the ttCkpt procedure to initiate a fuzzy checkpoint.

Connection conn;
CallableStatement cStmt;
.......
cStmt = conn.prepareCall("{ Call ttCkpt }");
cStmt.execute();
conn.commit();           // commit the transaction

Be aware that the ttCkpt built-in procedure requires ADMIN privilege. Refer to "ttCkpt" in Oracle TimesTen In-Memory Database Reference for additional information.

Example 2-9 Executing a ttDataStoreStatus call

This example calls the ttDataStoreStatus procedure and prints out the returned result set.

Contrary to the advice given in "Working with TimesTen result sets: hints and restrictions", this example uses a getString() call on the ResultSet object to retrieve the Context field, which is a binary. This is because the output is printed, rather than used for processing. If you do not want to print the Context value, you can achieve better performance by using the getBytes() method instead.

ResultSet rs;

CallableStatement cStmt = conn.prepareCall("{ Call ttDataStoreStatus }");

if (cStmt.execute() == true) {
    rs = cStmt.getResultSet();
    System.out.println("Fetching result set...");
    while (rs.next()) {
      System.out.println("\n Data store: " + rs.getString(1));
      System.out.println(" PID: " + rs.getInt(2));
      System.out.println(" Context: " + rs.getString(3));
      System.out.println(" ConType: " + rs.getString(4));
      System.out.println(" memoryID: " + rs.getString(5));
      }
    rs.close();
  }
cStmt.close();

Note:

In TimesTen, you cannot pass parameters to a CallableStatement object by name. You must set parameters by ordinal numbers. You cannot use the SQL escape syntax.

Working with OUT and IN OUT parameters

"Preparing SQL statements and setting input parameters" shows how to prepare a statement and set input parameters using PreparedStatement methods. TimesTen also supports OUT and IN OUT parameters, for which you use java.sql.CallableStatement instead of PreparedStatement, as follows.

  1. Use the method registerOutParameter() to register an OUT or IN OUT parameter, specifying the parameter position (position in the statement) and data type.

    This is the standard method as specified in the CallableStatement interface:

    void registerOutParameter(int parameterIndex, int sqlType, int scale)
    

    Be aware, however, that if you use this standard version for CHAR, VARCHAR, NCHAR, NVARCHAR, BINARY, or VARBINARY data types, TimesTen will allocate memory to hold the largest possible value. In many cases this is wasteful.

    Instead, you can use the TimesTen extended interface TimesTenCallableStatement, which includes a registerOutParameter() signature that allows you to specify the maximum data length in bytes:

    void registerOutParameter(int paramIndex,
                              int sqlType,
                              int ignore, //This parameter is ignored by TimesTen.
                              int maxLength)
    
  2. Use the appropriate CallableStatement method setXXX(), where XXX indicates the data type, to set the input value of an IN OUT parameter. Specify the parameter position and data value.

  3. Use the appropriate CallableStatement method getXXX() to get the output value of an OUT or IN OUT parameter, specifying the parameter position.

Important:

Check for SQL warnings before processing output parameters. In the event of a warning, output parameters are undefined. See "Handling errors" for general information about errors and warnings.

Notes:

In TimesTen:
  • The registerOutParameter() signatures specifying the parameter by name are not supported. You must specify the parameter by position.

  • SQL structured types are not supported.

Example 2-10 Using an OUT parameter in a callable statement

This example shows how to use a callable statement with an OUT parameter. In the TimesTenCallableStatement instance, a PL/SQL block calls a function RAISESAL that calculates a new salary and returns it as an integer. Assume a Connection instance conn. (Refer to Oracle TimesTen In-Memory Database PL/SQL Developer's Guide for information about PL/SQL.)

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Types;
import com.timesten.jdbc.TimesTenCallableStatement;
...
// Prepare to call a PL/SQL stored procedure RAISESAL (raise salary)
CallableStatement cstmt = conn.prepareCall
                          ("BEGIN :newSalary := RAISESAL(:name, :inc); end;");
      
// Declare that the first param (newSalary) is a return (output) value of type int
cstmt.registerOutParameter(1, Types.INTEGER);
 
// Raise Leslie's salary by $2000 (she wanted $3000 but we held firm)
cstmt.setString(2, "LESLIE"); // name argument (type String) is the second param
cstmt.setInt(3, 2000); // raise argument (type int) is the third param
 
// Do the raise
cstmt.execute();

// Check warnings. If there are warnings, values of OUT parameters are undefined.
SQLWarning wn;
boolean warningFlag = false;
if ((wn = cstmt.getWarnings() ) != null) {
   do {
        warningFlag = true;
        System.out.println(wn);
        wn = wn.getNextWarning();
   } while(wn != null);
}      
      
// Get the new salary back
if (!warningFlag) {
   int new_salary = cstmt.getInt(1);
   System.out.println("The new salary is: " + new_salary);
}

// Close the statement
cstmt.close();
      
conn.close();
...

Working with DML returning (RETURNING INTO clause)

You can use a RETURNING INTO clause, referred to as DML returning, with an INSERT, UPDATE, or DELETE statement to return specified items from a row that was affected by the action. This eliminates the need for a subsequent SELECT statement and separate round trip, in case, for example, you want to confirm what was affected by the action.

With TimesTen, DML returning is limited to returning items from a single-row operation. The clause returns the items into a list of OUT parameters.

TimesTenPreparedStatement, an extension of the standard PreparedStatement interface, supports DML returning.

Use the TimesTenPreparedStatement method registerReturnParameter() to register the return parameters. As with the registerOutParameter() method discussed in "Working with OUT and IN OUT parameters" above, this method includes a signature that allows you to optionally specify a maximum size of the data, in bytes. For CHAR, VARCHAR, NCHAR, NVARCHAR, BINARY, or VARBINARY data types, this avoids possible inefficiency where TimesTen would otherwise allocate memory to hold the largest possible value.

void registerReturnParameter(int paramIndex, int sqlType, int maxSize)

Use the TimesTenPreparedStatement method getReturnResultSet() to retrieve the return parameters, returning a ResultSet instance.

Be aware of the following restrictions.

  • The getReturnResultSet() method must not be invoked more than once. Otherwise, the behavior is indeterminate.

  • ResultSetMetaData is not supported for the result set returned by getReturnResultSet().

  • Streaming methods such as getCharacterStream() are not supported for the result set returned by getReturnResultSet().

  • There is no batch support for DML returning.

SQL syntax and restrictions for the RETURNING INTO clause in TimesTen are documented as part of the "INSERT", "UPDATE", and "DELETE" documentation in Oracle TimesTen In-Memory Database SQL Reference.

Refer to "RETURNING INTO Clause" in Oracle Database PL/SQL Language Reference for general information about DML returning.

Important:

Check for SQL warnings after executing the TimesTen prepared statement. In the event of a warning, output parameters are undefined. See "Handling errors" for general information about errors and warnings.

Example 2-11 DML returning

This example shows how to use DML returning with a TimesTenPreparedStatement instance, returning the name and age for a row that is inserted.

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Types;
import com.timesten.jdbc.TimesTenPreparedStatement;
 
      Connection conn;
 
      ...
 
      // Insert into a table and return results
      TimesTenPreparedStatement pstmt =
        (TimesTenPreparedStatement)conn.prepareStatement
        ("insert into tab1 values(?,?) returning name, age into ?,?");
 
      // Populate table
      pstmt.setString(1,"John Doe");
      pstmt.setInt(2, 65);
 
      /** register returned parameter
       * in this case the maximum size of name is 100 chars
       */
      pstmt.registerReturnParameter(3, Types.VARCHAR, 100);
      pstmt.registerReturnParameter(4, Types.INTEGER);
 
      // process the DML returning statement
      int count = pstmt.executeUpdate();
 
      /* Check warnings; if there are warnings, values of DML RETURNING INTO
         parameters are undefined. */
      SQLWarning wn;
      boolean warningFlag = false;
      if ((wn = pstmt.getWarnings() ) != null) {
        do {
          warningFlag = true;
          System.out.println(wn);
          wn = wn.getNextWarning();
        } while(wn != null);
      }
 
      if (!warningFlag) {
        if (count>0)
        {
          ResultSet rset = pstmt.getReturnResultSet(); //rset not null, not empty
          while(rset.next())
          {
            String name = rset.getString(1);
            int age = rset.getInt(2);
            System.out.println("Name " + name + " age " + age);
          }
        }
      }

Working with PL/SQL REF CURSORs

REF CURSOR is a PL/SQL concept, where a REF CURSOR is a handle to a cursor over a SQL result set and can be passed between PL/SQL and an application. In TimesTen, the cursor can be opened in PL/SQL, then the REF CURSOR can be passed to the application for processing of the result set.

An application can receive a REF CURSOR, as an OUT parameter, as follows:

  1. Register the REF CURSOR OUT parameter as type TimesTenTypes.CURSOR (a TimesTen type extension), also specifying the parameter position of the REF CURSOR (position in the statement).

  2. Retrieve the REF CURSOR using the getCursor() method defined by the TimesTenCallableStatement interface (a TimesTen JDBC extension), specifying the parameter position of the REF CURSOR. The getCursor() method is used like other getXXX() methods and returns a ResultSet instance.

Important:

For passing REF CURSORs between PL/SQL and an application, TimesTen supports only OUT REF CURSORs, from PL/SQL to the application, and supports a statement returning only a single REF CURSOR.

The following example demonstrates this usage.

Example 2-12 Using a REF CURSOR

This example shows how to use a callable statement with a REF CURSOR. In the CallableStatement instance, a PL/SQL block opens a cursor and executes a query. The TimesTenCallableStatement method getCursor() is used to return the cursor, which is registered as TimesTenTypes.CURSOR.

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import com.timesten.jdbc.TimesTenCallableStatement;
import com.timesten.jdbc.TimesTenTypes;
...
Connection conn;
CallableStatement cstmt;
ResultSet cursor;
...
// Use a PL/SQL block to open the cursor.
cstmt = conn.prepareCall
             (" begin open :x for select tblname,tblowner from tables; end;");
cstmt.registerOutParameter(1, TimesTenTypes.CURSOR);
cstmt.execute();
cursor = ((TimesTenCallableStatement)cstmt).getCursor(1);

// Use the cursor as you would any other ResultSet object.
while(cursor.next()){
  System.out.println(cursor.getString(1));
}

// Close the statement
cstmt.close();
conn.close();
...

Working with rowids

Each row in a TimesTen database table has a unique identifier known as its rowid. An application can retrieve the rowid of a row from the ROWID pseudocolumn. A rowid value can be represented in either binary or character format, with the binary format taking 12 bytes and the character format 18 bytes.

You can use any of the following ResultSet methods to retrieve a rowid:

  • byte[] getBytes(int columnIndex)

  • String getString(int columnIndex)

  • Object getObject(int columnIndex) (returning a String object)

You can use any of the following PreparedStatement methods to set a rowid:

  • setBytes(int parameterIndex, byte[] x)

  • setString(int parameterIndex, String x)

  • setObject(int parameterIndex, Object x) (taking a String object)

Note:

You cannot use getBytes() or setBytes() for ROWID parameters that are PL/SQL parameters or passthrough parameters (parameters passed to Oracle when using the Oracle In-Memory Database Cache). Use getString() and setString(), or use getObject() and setObject() with a String object.

An application can specify literal rowid values in SQL statements, such as in WHERE clauses, as CHAR constants enclosed in single quotes.

Refer to "ROWID data type" and "ROWID specification" in Oracle TimesTen In-Memory Database SQL Reference for additional information about rowids and the ROWID data type, including usage and lifecycle.

Note:

Oracle TimesTen In-Memory Database does not support the PL/SQL type UROWID.

Fetching multiple rows of data

Fetching multiple rows of data from a TimesTen data store can increase the performance of an application that connects to a data store set with read committed isolation.

You can specify the number of rows to be prefetched as follows:

  • Call the Statement or ResultSet method setFetchSize(). These are the standard JDBC calls, but the limitation is that they only affect one statement at a time.

  • Call the TimesTenConnection method setTtPrefetchCount(). This enables a TimesTen extension that establishes prefetch on a connection level so that all of the statements on the connection use the same prefetch setting.

This section describes the connection-level prefetch implemented in TimesTen.

Note:

You can use the TimesTen prefetch count extension only with direct-linked applications.

When the prefetch count is set to 0, TimesTen uses a default value, depending on the Isolation level you have set for the data store. In read committed isolation mode, the default prefetch value is 5. In serializable isolation mode, the default is 128. The default prefetch value is the optimum setting for most applications. Generally, a higher value may result in better performance for larger result sets, at the expense of slightly higher resource use.

To disable prefetch, set the prefetch count to 1.

Call the TimesTenConnection method getTtPrefetchCount() to check the current prefetch value.

Example 2-13 Setting a prefetch count

The following code uses a setTtPrefetchCount() call to set the prefetch count to 10, then uses a getTtPrefetchCount() call to return the prefetch count in the count variable.

TimesTenConnection conn =
    (TimesTenConnection) DriverManager.getConnection(url);

// set prefech count to 10 for this connection
conn.setTtPrefetchCount(10);

// Return the prefetch count to the 'count' variable.
int count = conn.getTtPrefetchCount();

Setting a timeout or threshold for executing SQL statements

TimesTen offers two ways to limit the time for SQL statements to execute, applying to any execute(), executeBatch(), executeQuery(), executeUpdate(), or next() call.

The former is to set a timeout, where if the timeout duration is reached, the statement stops executing and an error is thrown. The latter is to set a threshold, where if the threshold is reached, an SNMP trap is thrown but execution continues.

Setting a timeout value for SQL statements

In TimesTen you can set the SqlQueryTimeout DSN attribute to specify the timeout period (in seconds) for any connection, and hence any statement. If you set SqlQueryTimeout in the DSN specification, its value becomes the default value for all subsequent connections to the data store. Despite the name, this timeout value applies to any executable SQL statement, not just queries.

For a particular statement, you can override the SqlQueryTimeout setting by calling the Statement method setQueryTimeout().

The query timeout limit has effect only when the SQL statement is actively executing. A timeout does not occur during the commit or rollback phase of an operation. For those transactions that execute a large number of UPDATE, DELETE, or INSERT statements, the commit or rollback phases may take a long time to complete. During that time the timeout value is ignored.

Note:

If both a lock-wait and a SqlQueryTimeout are specified, the lesser of the two values causes a timeout first. Regarding lock timeouts, in Oracle TimesTen In-Memory Database Reference you can refer to information about the ttLockWait built-in procedure in "ttLockWait" and about the LockWait connection attribute in "LockWait". Or refer to "Check for deadlocks and timeouts" in the Oracle TimesTen In-Memory Database Troubleshooting Procedures Guide.

Setting a threshold value for SQL statements

You can configure TimesTen to write a warning to the support log and throw an SNMP trap when the execution of a SQL statement exceeds a specified time duration, in seconds. Execution continues and is not affected by the threshold.

The name of the SNMP trap is ttQueryThresholdWarnTrap. See Oracle TimesTen In-Memory Database Error Messages and SNMP Traps for information about configuring SNMP traps.

Despite the name, this threshold applies to any JDBC call executing a SQL statement, not just queries.

By default, the application obtains the threshold value from the QueryThreshold connection attribute setting. You can override the threshold for a JDBC Connection object by including the QueryThreshold attribute in the connection URL for the data store. For example, to set QueryThreshold to 5 seconds for myDSN data store:

jdbc:timesten:direct:dsn=myDSN;QueryThreshold=5

You can also use the setQueryTimeThreshold() method of a TimesTenStatement object to set the threshold. This overrides the connection attribute setting and the Connection object setting.

You can retrieve the current threshold value by using the getQueryTimeThreshold() method of the TimesTenStatement object.

Escape syntax in SQL functions

When using SQL in JDBC, pay special attention to Java escape syntax. SQL functions such as UNISTR use the backslash (\) character. You should escape the backslash character. For example, using the following SQL syntax in a Java application may not produce the intended results:

INSERT INTO table1 SELECT UNISTR('\00E4') FROM dual;

Escape the backslash character as follows:

INSERT INTO table1 SELECT UNISTR('\\00E4') FROM dual;

Managing cache groups

In TimesTen, following the execution of a FLUSH CACHE GROUP, LOAD CACHE GROUP, REFRESH CACHE GROUP, or UNLOAD CACHE GROUP statement, the Statement method getUpdateCount() returns the number of cache instances that were flushed, loaded, refreshed, or unloaded.

For related information, see "Determining the number of cache instances affected by an operation" in Oracle In-Memory Database Cache User's Guide.

Committing changes

This section discusses autocommit and manual commits or rollbacks, assuming a JDBC Connection object myconn.

Note:

All open cursors are closed upon transaction commit or rollback in TimesTen.

Setting autocommit

A TimesTen connection has autocommit enabled by default, but it is recommended that you disable it. You can use the Connection method setAutoCommit() to enable or disable autocommit.

To disable autocommit:

myconn.setAutoCommit(false);
// Report any SQLWarnings on the connection
// See "Reporting errors and warnings"

Manually committing or rolling back changes

If autocommit is disabled, you must use the Connection method commit() to manually commit transactions, or the rollback() method to roll back changes. For example:

myconn.commit();

Or:

myconn.rollback();

Using COMMIT and ROLLBACK SQL statements

You can prepare and execute COMMIT and ROLLBACK SQL statements the same way as other SQL statements. Using COMMIT and ROLLBACK SQL statements has the same effect as using the Connection methods commit() and rollback().

Managing multiple threads

Note:

On some UNIX platforms it is necessary to set THREADS_FLAG, as described in "Set the THREADS_FLAG variable (UNIX only)" in Oracle TimesTen In-Memory Database Installation Guide.

The level4 demo demonstrates the use of multiple threads. Refer to "About the TimesTen Java demos".

When your application has a direct driver connection to the data store, TimesTen functions share stack space with your application. In multithreaded environments, it is important to avoid overrunning the stack allocated to each thread because consequences can result that are unpredictable and difficult to debug. The amount of stack space consumed by TimesTen calls varies depending on the SQL functionality used. Most applications should set thread stack space to at least 16 KB on 32-bit systems and between 34 KB to 72 KB on 64-bit systems.

The amount of stack space allocated for each thread is specified by the operating system when threads are created. On Windows, you can use the TimesTen debug driver and link your application against the Visual C++ debug C library to enable "stack probes" that raise an identifiable exception if a thread attempts to grow its stack beyond the amount allocated.

Note:

In multithreaded applications, a thread that issues requests on different connection handles to the same data store may encounter lock conflict with itself. TimesTen resolves these conflicts with lock timeouts.

Considering TimesTen features for access control

TimesTen has features to control database access with object-level resolution for database objects such as tables, views, materialized views, and sequences. You can refer to "Managing Access Control" in Oracle TimesTen In-Memory Database Operations Guide for introductory information about TimesTen access control.

This section introduces access control as it relates to SQL operations, data store connections, and JMS/XLA.

For any query, SQL DML statement, or SQL DDL statement discussed in this document or used in an example, it is assumed that the user has appropriate privileges to execute the statement. For example, a SELECT statement on a table requires ownership of the table, SELECT privilege granted for the table, or the SELECT ANY TABLE system privilege. Similarly, any DML statement requires table ownership, the applicable DML privilege (such as UPDATE) granted for the table, or the applicable ANY TABLE privilege (such as UPDATE ANY TABLE).

For DDL statements, CREATE TABLE requires the CREATE TABLE privilege in the user's schema, or CREATE ANY TABLE in any other schema. ALTER TABLE requires ownership or the ALTER ANY TABLE system privilege. DROP TABLE requires ownership or the DROP ANY TABLE system privilege. There are no object-level ALTER or DROP privileges.

Refer to "SQL Statements" in Oracle TimesTen In-Memory Database SQL Reference for a list of access control privileges and the privilege required for any given SQL statement.

Privileges are granted through the SQL statement GRANT and revoked through the statement REVOKE. Some privileges are automatically granted to all users through the PUBLIC role, of which all users are a member. Refer to "The PUBLIC role" in Oracle TimesTen In-Memory Database SQL Reference for information about this role.

In addition, access control affects the following topics covered in this document:

Notes:

  • Access control cannot be disabled.

  • Access control privileges are checked both when SQL is prepared and when it is executed in the database, with most of the performance cost coming at prepare time.

Handling errors

This section discusses how to check for, identify and handle errors in a TimesTen Java application.

For a list of the errors that TimesTen returns and what to do if the error is encountered, see "Warnings and Errors" in Oracle TimesTen In-Memory Database Error Messages and SNMP Traps.

This section includes the following topics:

About fatal errors, non-fatal errors, and warnings

TimesTen can return a fatal error, a non-fatal error, or a warning.

Handling fatal errors

Fatal errors make the data store inaccessible until it can be recovered. When a fatal error occurs, all data store connections are required to disconnect. No further operations may complete. Fatal errors are indicated by TimesTen error codes 846 and 994. Error handling for these errors should be different from standard error handling. In particular, the code should rollback the transaction and disconnect from the data store.

When fatal errors occur, TimesTen performs the full cleanup and recovery procedure:

  • Every connection to the data store is invalidated, a new memory segment is allocated and applications are required to disconnect.

  • The data store is recovered from the checkpoint and transaction log files upon the first subsequent initial connection.

    • The recovered data store reflects the state of all durably committed transactions and possibly some transactions that were committed non-durably.

    • No uncommitted or rolled back transactions are reflected.

If no checkpoint or transaction log files exist and the AutoCreate attribute is set, TimesTen creates an empty data store.

Handling non-fatal errors

Non-fatal errors include simple errors such as an INSERT that violates unique constraints. This category also includes some classes of application and process failures.

TimesTen returns non-fatal errors through the normal error-handling process and requires the application to check for and identify them.

When a data store is affected by a non-fatal error, an error may be returned and the application should take appropriate action. In some cases, such as with a process failure, an error cannot be returned, so TimesTen automatically rolls back the transactions of the failed process.

An application can handle non-fatal errors by modifying its actions or, in some cases, by rolling back one or more offending transactions, as described in "Rolling back failed transactions".

Note:

If a ResultSet, Statement, PreparedStatement, CallableStatement or Connection operation results in a data store error, it is a good practice to call the close() method for that object.

About warnings

TimesTen returns warnings when something unexpected occurs that you may want to know about. Here are some examples of events that cause TimesTen to issue a warning:

  • A checkpoint failure

  • Use of a deprecated TimesTen feature

  • Truncation of some data

  • Execution of a recovery process upon connect

You should always include code that checks for warnings, as they can indicate application problems.

Reporting errors and warnings

You should check for and report all errors and warnings that can be returned on every call. This saves considerable time and effort during development and debugging. A SQLException object is generated in case of one or more data store access errors and a SQLWarning object is generated in the case of one or more warning messages. A single call may return multiple errors or warnings or both, so your application should report all errors or warnings in the returned SQLException or SQLWarning objects.

Multiple errors or warnings are returned in linked chains of SQLException or SQLWarning objects. Example 2-14 and Example 2-15 demonstrate how you might iterate through the lists of returned SQLException and SQLWarning objects to report all of the errors and warnings, respectively.

Example 2-14 Printing exceptions

This method prints out the content of all exceptions in the linked SQLException objects.

static int reportSQLExceptions(SQLException ex)
  {
    int errCount = 0;

    if (ex != null) {
      errStream.println("\n--- SQLException caught ---");
      ex.printStackTrace();

      while (ex != null) {
        errStream.println("SQL State: " + ex.getSQLState());
        errStream.println("Message: " + ex.getMessage());
        errStream.println("Error Code: " + ex.getErrorCode());
        errCount ++;
        ex = ex.getNextException();
        errStream.println();
      }
    }

    return errCount;
}

Example 2-15 Printing warnings

This method prints out the content of all warning in the linked SQLWarning objects.

static int reportSQLWarnings(SQLWarning wn)
{
    int warnCount = 0;

    while (wn != null) {
      errStream.println("\n--- SQL Warning ---");
      errStream.println("SQL State: " + wn.getSQLState());
      errStream.println("Message: " + wn.getMessage());
      errStream.println("Error Code: " + wn.getErrorCode());

      // is this a SQLWarning object or a DataTruncation object?
      if (wn instanceof DataTruncation) {
        DataTruncation trn = (DataTruncation) wn;
        errStream.println("Truncation error in column: " +
          trn.getIndex());
      }

      warnCount++;
      wn = wn.getNextWarning();
      errStream.println();
    }

    return warnCount;
}

Catching and responding to specific errors

In some situations it may be desirable to respond to a specific SQL state or TimesTen error code. You can use the SQLException method getSQLState() to return the SQL99 SQL state error string, and getErrorCode() to return TimesTen error codes, as shown in Example 2-16.

Also refer to the entry for TimesTenVendorCode in Oracle TimesTen In-Memory Database JDBC Extensions Java API Reference for error information.

Example 2-16 Catching an error

The TimesTen demos require that you load the demo schema before they are executed. The following catch statement alerts the user that appuser has not been loaded or has not been refreshed by detecting ODBC error S0002 and TimesTen error 907:

catch (SQLException ex) {
  if (ex.getSQLState().equalsIgnoreCase("S0002")) {
    errStream.println("\nError: The table appuser.customer " +
      "does not exist.\n\t Please reinitialize the database.");
  } else if (ex.getErrorCode() == 907) {
    errStream.println("\nError: Attempting to insert a row " +
      "with a duplicate primary key.\n\tPlease reinitialize the database.");
}

You can use the TimesTenVendorCode interface to detect the errors by their name, rather than their number.

Consider the following example:

ex.getErrorCode() == com.timesten.jdbc.TimesTenVendorCode.TT_ERR_KEYEXISTS

This is equivalent:

ex.getErrorCode() == 907

Rolling back failed transactions

In some situations, such as recovering from a deadlock or time-out condition, you may want to explicitly roll back the transaction using the Connection.rollback() method, as in the following example.

Example 2-17 Rolling back a transaction

try {
  if (conn != null && !conn.isClosed()) {
    // Rollback any transactions in case of errors
      if (retcode != 0) {
        try {
          System.out.println("\nEncountered error. Rolling back transaction");
          conn.rollback();
        } catch (SQLException ex) {
          reportSQLExceptions(ex);
        }
      }
   }

    System.out.println("\nClosing the connection\n");
    conn.close();
}

The XACT_ROLLBACKS column of the SYS.MONITOR table indicates the number of transactions that were rolled back.

A transaction rollback consumes resources and the entire transaction is in effect wasted. To avoid unnecessary rollbacks, design your application to avoid contention and check application or input data for potential errors before submitting it, whenever possible.

Note:

If your application fails in the middle of an active transaction, TimesTen automatically rolls back the transaction.

JDBC support for automatic client failover

Automatic client failover, used in High Availability scenarios when failure of a TimesTen node results in failover (transfer) to an alternate node, automatically reconnects applications to the new node. TimesTen provides features that allow applications to be alerted when this happens, so they can take any appropriate action.

This section discusses TimesTen JDBC extensions related to automatic client failover, covering the following topics:

Note:

Automatic client failover applies only to client/server mode. The functionality described here does not apply to a direct connection.

Automatic client failover is complementary to Oracle Clusterware, though the two features are not dependent on each other.

You can refer to "Automatic client failover" in Oracle TimesTen In-Memory Database C Developer's Guide for related information. You can also refer to "Using Oracle Clusterware to Manage Active Standby Pairs" in Oracle TimesTen In-Memory Database TimesTen to TimesTen Replication Guide for information about Oracle Clusterware.

Features and functionality of JDBC support for automatic client failover

This section discusses general TimesTen JDBC features related to client failover, as well as functionality relating specifically to pooled connections.

General Client Failover Features

TimesTen JDBC support for automatic client failover provides two mechanisms for detecting a failover:

  • Synchronous detection, through a SQL exception. After an automatic client failover, JDBC objects created on the failed connection—such as statements, prepared statements, callable statements, and result sets—can no longer be used. A Java SQL exception is thrown if an application attempts to access any such object. By examining the SQL state and error code of the exception, you can determine whether the exception is the result of a failover situation.

  • Asynchronous detection, through an event listener. An application can register a user-defined client failover event listener, which will be notified of each event that occurs during the process of a failover.

TimesTen JDBC provides the following features, in package com.timesten.jdbc, to support automatic client failover:

  • The ClientFailoverEvent class. This class is used to represent events that occur during a client failover: begin, end, abort, or retry.

  • The ClientFailoverEventListener interface. An application interested in client failover events must include a class that implements this interface, which is the mechanism to listen for client failover events. At runtime, the application must register ClientFailoverEventListener instances through the TimesTen connection (see immediately below).

  • New methods in the TimesTenConnection interface. This interface specifies the methods addConnectionEventListener() and removeConnectionEventListener() to register or remove, respectively, a client failover event listener.

  • A new constant, TT_ERR_FAILOVERINVALIDATION, in the TimesTenVendorCode interface. This allows you to identify an event as a failover event.

Client Failover Features for Pooled Connections

TimesTen recommends that applications using pooled connections (javax.sql.PooledConnection) or connection pool data sources (javax.sql.ConnectionPoolDataSource) use the synchronous mechanism noted previously to handle stale objects on the failed connection. Java EE application servers manage pooled connections, so applications are not able to listen for events on pooled connections. And application servers would not implement and register an instance of ClientFailoverEventListener, that being a TimesTen extension.

Synchronous detection of automatic client failover

If, in a failover situation, an application attempts to use objects created on the failed connection, then JDBC will throw a SQL exception. The vendor-specific exception code will be set to TimesTenVendorCode.TT_ERR_FAILOVERINVALIDATION.

Detecting a failover through this mechanism is referred to as synchronous detection. The following example demonstrates this.

Example 2-18 Synchronous detection of automatic client failover

try {

   // ...
   // Execute a query on a previously prepared statement.
   ResultSet theResultSet = theStatement.executeQuery("select * from dual");
   // ...

} catch (SQLException sqlex) {
   sqlex.printStackTrace();
   if (sqlex.getErrorCode() == TimesTenVendorCode.TT_ERR_FAILOVERINVALIDATION) {
   // Automatic client failover has taken place; discontinue use of this object.
   }
}

Asynchronous detection of automatic client failover

Asynchronous failover detection requires an application to implement a client failover event listener and register an instance of it on the TimesTen connection. This section describes the steps involved:

  1. Implement a client failover event listener

  2. Register the client failover listener instance

  3. Remove the client failover listener instance

Implement a client failover event listener

TimesTen JDBC provides the com.timesten.jdbc.ClientFailoverEventListener interface for use in listening for events, highlighted by the following method:

  • void notify(ClientFailoverEvent event)

To use asynchronous failover detection, you must create a class that implements this interface, then register an instance of the class at runtime on the TimesTen connection (discussed shortly).

When a failover event occurs, TimesTen calls the notify() method of the listener instance you registered, providing a ClientFailoverEvent instance that you can then examine for information about the event.

The following example shows the basic form of a ClientFailoverEventListener implementation.

Example 2-19 Asynchronous detection of automatic client failover

private class MyCFListener implements ClientFailoverEventListener {
      // Skeletal example
      /* Applications can build state system to track states during failover.
         You may want to add methods that talks about readiness of this Connection
         for processing. 
      */
      public void notify(ClientFailoverEvent event) {
         
         // Process connection failover type
         switch(event.getTheFailoverType()) {
         case TT_FO_CONNECTION:
            // Process session fail over
            System.out.println("This should be a connection failover type " +
                                event.getTheFailoverType());
            break;
            
         default:
            break;
            
         }
         
         // Process connection failover events
         switch(event.getTheFailoverEvent()) {
         case BEGIN:
            System.out.println("This should be a BEGIN event " +
                                event.getTheFailoverEvent());
            
            /* Applications cannot use Statement, PreparedStatement, ResultSet,
               etc. created on the failed Connection any longer.
            */
            // ...
            break;
            
         case END:
            System.out.println("This should be an END event " +
                                event.getTheFailoverEvent());
            
            /* Applications may want to re-create Statement and PreparedStatement
               objects at this point as needed.
            */
 
            break;
         
         case ABORT:
            System.out.println("This should be an ABORT event " +
                                event.getTheFailoverEvent());
            break;
            
         case ERROR:
            System.out.println("This should be an ERROR event " +
                                event.getTheFailoverEvent());
            break;
            
         default:
            break;
         }
      }      
   }

The event.getTheFailoverType() call returns an instance of the nested class ClientFailoverEvent.FailoverType, which is an enumeration type. In TimesTen, the only supported value is TT_FO_CONNECTION, indicating a connection failover.

The event.getTheFailoverEvent() call returns an instance of the nested class ClientFailoverEvent.FailoverEvent, which is an enumeration type where the value can be one of the following:

  • BEGIN if the client failover has begun.

  • END if the client failover has completed successfully.

  • ERROR if the client failover failed but will be retried.

  • ABORT if the client failover has aborted.

Register the client failover listener instance

At runtime you must register an instance of your failover event listener class with the TimesTen connection object, so that TimesTen will be able to call the notify() method of the listener class as needed for failover events.

The TimesTenConnection class provides the following method for this:

  • void addConnectionEventListener (ClientFailoverEventListener listener)

Create an instance of your listener class, then register it using this method. The following example establishes the connection and registers the listener. Assume theDsn is the JDBC URL for a TimesTen Client/Server data store and theCFListener is an instance of your failover event listener class.

Example 2-20 Registering the client failover listener

try {
         
         // Assume this is a client/server connection; register for conn failover.
         Class.forName("com.timesten.jdbc.TimesTenClientDriver");
         String url = "jdbc:timesten:client:" + theDsn;
         theConnection = (TimesTenConnection)DriverManager.getConnection(url);
         theConnection.addConnectionEventListener(theCFListener);
         // ...
         /* Additional logic goes here; connection failover listener will be
            called if there is a fail over.
         */
         // ...
      }
      catch (ClassNotFoundException cnfex) {
         cnfex.printStackTrace();
      }
      catch (SQLException sqlex) {
         sqlex.printStackTrace();
      }

Remove the client failover listener instance

The TimesTenConnection interface defines the following method to deregister a failover event listener:

  • void removeConnectionEventListener (ClientFailoverEventListener listener)

Use this method if you want to deregister a listener instance.