この章では、Javaアプリケーションを作成してTimesTenデータ・ストアのデータにアクセスする基本的な手順について説明します。TimesTenアプリケーションを作成する前に、前提条件となる次のタスクがすべて完了している必要があります。
| 前提条件タスク | 実行内容 |
|---|---|
| TimesTenデータ・ストアの作成 | 『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTenデータ・ストアの作成に関する章の手順に従います。 |
| Java環境の構成 | 「Java環境変数の設定」の手順に従います。 |
| TimesTen Javaのデモのコンパイルおよび実行 | 「TimesTen Javaのデモ」の手順に従います。 |
TimesTen Javaのデモを実行すると、開発環境が適切に設定され、TimesTenデータ・ストアにアクセスするアプリケーションを作成できます。
この章の内容は次のとおりです。
この項では、標準およびTimesTen固有の両方における、重要なJDBCパッケージ、クラスおよびインタフェースについて説明します。 次の内容を含みます。
標準JDBCについてのリファレンス情報は、次のURLを参照してください。
http://java.sun.com/j2se/1.5.0/docs/api/index.html
TimesTen JDBC拡張のリファレンス情報は、『Oracle TimesTen In-Memory Database JDBC Extensions Java API Reference』を参照してください。
JDBCを使用するJavaプログラムには、標準のJDBCパッケージをインポートする必要があります。
import java.sql.*;
データ・ソースまたはプールされている接続を使用する場合は、標準拡張JDBCパッケージもインポートする必要があります。
import javax.sql.*;
TimesTen JDBCパッケージをインポートする必要があります。
import com.timesten.jdbc.*;
JTAにXAデータ・ソースを使用するには、次のTimesTenパッケージもインポートする必要があります。
import com.timesten.jdbc.xa.*;
TimesTenでは、表2-1で示されるjava.sqlインタフェースをサポートしています。
表2-1 サポートされているjava.sqlインタフェース
| java.sqlのインタフェース | TimesTenでのサポートにおける注意事項 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TimesTenでは、次のjava.sqlクラスをサポートしています。
Date
DriverManager
DriverPropertyInfo
Time
Timestamp
Types
DataTruncation
SQLException
SQLWarning
TimesTenでは、次のjavax.sqlインタフェースをサポートしています。
DataSourceはTimesTenDataSourceで実装されています。
PooledConnectionはObservableConnectionで実装されています。
ConnectionPoolDataSourceはObservableConnectionDSで実装されています。
XADataSourceはTimesTenXADataSource(com.timesten.jdbc.xaパッケージ内)で実装されています。
|
重要: TimesTen JDBCドライバ自身は、データベース接続プールを実装していません。ObservableConnectionおよびObservableConnectionDSクラスは、標準Java EEインタフェースを実装しているだけであり、Java EE標準に基づいたデータベース接続プールの作成および管理を簡単に行えます。 |
ほとんどの場合、TimesTenでサポートされている標準JDBC機能を使用することができます。
また、TimesTen固有の機能のためのcom.timesten.jdbcパッケージには、次の拡張機能もあります。
| インタフェース | 拡張 | 注意事項 |
|---|---|---|
|
|
行のプリフェッチによるパフォーマンス改善、イベントのリスニングによる自動クライアント・フェイルオーバーなどの機能を提供します。 詳細は、「複数のデータ行のフェッチ」および「一般的なクライアント・フェイルオーバー機能」を参照してください。 |
|
|
|
問合せのしきい値を指定する機能を提供します。 「SQL文のしきい値の設定」を参照してください。 |
|
|
|
DML RETURNINGのサポート。 「DML RETURNINGの使用(RETURNING INTO句)」を参照してください。 |
|
|
|
PL/SQL REF CURSORのサポート。 「PL/SQL REF CURSORの使用」を参照してください。 |
前述の実装に加えて、com.timesten.jdbcパッケージには、次のクラスおよびインタフェースがあります。 これらのクラスおよびインタフェースでサポートされた機能は、この章の後半で説明します。
REF CURSORのためのTimesTen型の拡張に、TimesTenTypesを使用します。
自動クライアント・フェイルオーバー機能に、ClientFailoverEventListener(および次に説明するClientFailoverEventクラス)を使用します。 「自動クライアント・フェイルオーバーのJDBCサポート」を参照してください。
SQL例外に使用されるベンダー・コードに、TimesTenVendorCodeを使用します。
自動クライアント・フェイルオーバー機能に、ClientFailoverEvent(および前述のClientFailoverEventListenerインタフェース)を使用します。
作成するDSNのタイプは、アプリケーションがデータ・ストアに、直接接続するか、クライアント接続するかによって異なります。 データ・ストアに直接接続する場合は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のUNIXでのDSNの作成またはWindowsでのDSNの作成に関する項を参照してください。 データ・ストアへのクライアント接続を作成するには、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のWindowsでのクライアントDSNの作成および設定またはUNIXでのクライアントDSNの作成および設定に関する項の説明に従って、DNSを作成します。
DSNを作成すると、アプリケーションでデータ・ストアに接続できます。この項では、JDBC直接ドライバまたはJDBCクライアント・ドライバを使用して、データ・ストアへのJDBC接続を作成する方法について説明します。
この項で説明する操作は、level1デモに基づいています。 「TimesTen Javaのデモ」を参照してください。
ここでは次の内容について説明します。
TimesTenデータ・ストアへの接続にTimesTen JDBCドライバを使用するには、このドライバをロードしておく必要があります。TimesTen JDBCドライバは、次のとおりです。
com.timesten.jdbc.TimesTenDriver
TimesTenへの接続にDriverManagerインタフェースを使用している場合、Class.forName()メソッドをコールして、TimesTen JDBCドライバをロードします。 このメソッドによって、TimesTenドライバのインスタンスが作成され、ドライバ・マネージャに登録されます。 TimesTenDataSourceインタフェースを使用している場合、Class.forName()をコールする必要はありません。
TimesTenドライバを識別し、ロードするには、次のように入力します。
Class.forName("com.timesten.jdbc.TimesTenDriver");
|
注意: TimesTen JDBCドライバがロードされていないとき、アプリケーションがTimesTenデータ・ストアに接続しようとするとエラーが返されます。 |
JDBC接続を作成するには、データ・ストアのTimesTen接続URLを指定する必要があります。 TimesTen接続URLの形式は、次のとおりです。
jdbc:timesten:{direct|client}:dsn=DSNname;[DSNattributes;]
デフォルトはdirectです。
たとえば、次のようにサンプル・データ・ストアへの直接接続を作成します。
String URL = "jdbc:timesten:direct:dsn=sampledb_1121";
接続URLに属性を指定して、DSNの記述に接続属性をプログラム的に設定または上書きすることができます。
接続属性の概要については、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のData Manager DSNの接続属性に関する項を参照してください。 一般的な接続属性に特別な権限は必要ありません。 データベースが最初にロードされたときに、最初の接続属性が設定された後、すべての接続で有効になります。 インスタンス管理者のみが、データベースをロードして最初の接続属性の設定を変更できます。 特定の接続属性(必要な権限を含む)の詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のデータ・ストア属性に関する項を参照してください。
たとえば、LockLevel DSNの一般的な接続属性を1に設定するには、次のようにURLを作成します。
String URL = "jdbc:timesten:direct:dsn=sampledb_1121;LockLevel=1";
URLを定義すると、DriverManagerまたはTimesTenDataSourceのgetConnection()メソッドを使用して、TimesTenデータ・ストアに接続できます。
DriverManager.getConnection()メソッドを使用する場合は、ドライバURLを指定してデータ・ストアに接続します。
import java.sql.*; ... Connection conn = DriverManager.getConnection(URL);
TimesTenDataSourceメソッドgetConnection()を使用するには、まずデータ・ソースを作成します。 次に、TimesTenDataSourceメソッドsetUrl()を使用してURLを設定し、getConnection()を使用して接続します。
import com.timesten.jdbc.TimesTenDataSource; import java.sql.*; ... TimesTenDataSource ds = new TimesTenDataSource(); ds.setUrl(URL); Connection conn = ds.getConnection();
いずれかのメソッドが、データ・ストアへのハンドルとして使用できるConnectionオブジェクト(この例では(conn)を返します。 DriverManagerメソッドgetConnection()の使用例についてはlevel1デモを、TimesTenDataSourceメソッドgetConnection()の使用例についてはlevel2デモおよびlevel3デモを参照してください。 「TimesTen Javaのデモ」を参照してください。
|
注意: TimesTen JDBCドライバがロードされていないとき、アプリケーションがTimesTenデータ・ストアに接続しようとするとエラーが返されます。 「TimesTenドライバのロード」を参照してください。 |
TimesTenデータ・ストアへのアクセスを終了する場合は、Connectionメソッドclose()をコールして、データ・ストアへの接続をクローズします。
エラーが発生した場合は、データ・ストアから切断する前に、トランザクションをロールバックできます。 詳細は、「致命的ではないエラーの処理」および「失敗したトランザクションのロールバック」を参照してください。
例2-1に、DriverManagerクラスを使用してサンプル・データ・ストアへの直接ドライバ接続を作成し、SQLを実行した後に接続をクローズするアプリケーションの一般的なフレームワークを示します。 処理の例については、level1デモを参照してください。 (デモについては、「TimesTen Javaのデモ」を参照してください。)
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"
}
この項では、TimesTenデータ・ストアのデータの処理について説明します。内容は次のとおりです。
SQLを使用してTimesTenデータ・ストアのデータを管理する方法については、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTenデータ・ストアのデータの処理に関する章を参照してください。 この項では、ConnectionインスタンスのcreateStatement()メソッド、およびStatementインスタンスのexecuteUpdate()メソッドまたはexecuteQuery()メソッドを使用して、Javaアプリケーション内でSQL文を実行する方法について説明します。
事前に文が準備されていない場合は、SQL文の特性および返された結果セットに応じて、execute()、executeUpdate()、executeQuery()などのStatementオブジェクトの実行メソッドを使用します。
事前に準備されたSQL文には、PreparedStatementオブジェクトの同じ実行メソッドを使用します。
execute()メソッドは、結果セットがある場合はtrueを返し(SELECTなど)、結果セットがない場合はfalseを返します(INSERT、UPDATE、DELETEなど)。 executeUpdate()メソッドは、影響を受ける行数を返します。たとえば、INSERT文を実行すると、executeUpdate()メソッドは挿入された行数を返します。 executeQuery()メソッドは結果セットを返すため、結果セットが必要な場合(SELECTの実行時など)にのみコールする必要があります。
|
注意:
|
この例では、StatementオブジェクトでexecuteUpdate()メソッドを使用して、現在のスキーマのcustomer表にINSERTを実行します。 示されてはいませんが、接続はオープンである必要があります。 「データ・ストアへの接続」を参照してください。
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) { ... }
この例では、StatementオブジェクトでexecuteQuery()コールを使用し、現在のスキーマのcustomer表にSELECTを実行して、返されたjava.sql.ResultSetインスタンスを表示します。
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();
}
ResultSetオブジェクトを使用して、問合せ結果を処理します。 また、一部のメソッドおよび組込みプロシージャは、ResultSetオブジェクトの形式でTimesTenデータを返します。 この項では、TimesTenからのResultSetオブジェクトの使用について説明します。
TimesTenでは、1つの文で複数のResultSetオブジェクトをオープンすることはできません。また、1つのStatementオブジェクトから、現行結果セットをクローズせずに複数のResultSetオブジェクトを返すことはできません。
TimesTenでは、保持可能なカーソルはサポートされていません。 結果セットの保持可能性(つまり、カーソルのコミット後に、そのカーソルをオープンしたままにできるかどうか)は指定できません。
ResultSetオブジェクトをスクロールまたは更新できないため、ResultSet.TYPE_SCROLL_SENSITIVEまたはResultSet.CONCUR_UPDATABLEは指定できません。
ResultSetメソッドclose()を使用して、処理後すぐに結果セットをクローズできます。
基礎となるデータ型が文字列ではない場合、ResultSetメソッドgetString()をコールすると、パフォーマンスの点でコストが高くなります。 Javaの文字列は不変的であるため、getString()は、コールされるたびに新しい文字列用に領域を確保する必要があります。 そのため、getString()は、JDBCではよりコストのかかる処理の1つとなります。 したがって、必要な場合を除き、byteやintなどの基本的な数値型の取得にgetString()を使用しないでください。 たとえば、整数列の場合、getInt()をコールする方が大幅に高速に処理が実行されます。
また、日付およびタイムスタンプでは、ResultSetネイティブ・メソッドのgetDate()およびgetTimestamp()の方が、getString()よりも一般にパフォーマンスが優れています。
getXXX()コールを選択することは、起動後に必要なデータ変換を選択することと同様にアプリケーション・パフォーマンスに影響を与えます。
JDBCはConnectionCharacterSet属性の設定を無視します。 JDBCはデータをUTF-16エンコードで返します。
複数回実行するSQL文は、ConnectionメソッドprepareStatement()をコールして事前に準備しておく必要があります。最大のパフォーマンスを得るために、パラメータを使用した文を準備します。
|
注意:
|
例2-4 準備済の文での問合せの実行
この例では、PreparedStatementオブジェクトでの基本的なexecuteQuery()コールを示します。 これは、準備済のSELECT文を実行して、返された結果セットを表示します。
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();
}
例2-5 パラメータを使用した文の使用(単一の接続)
次の例に、4つの別々の文の代わりに、パラメータを使用した単一の文を使用する方法を示します。
値が異なる同様のINSERT文の実行:
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)");
パラメータを使用した単一のINSERT文を準備し、PreparedStatementメソッドsetXXX()を使用して、各文を実行する前に行の値を設定する方がより効率的です。
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では、準備された文がコミットされると、自動的に共有されます。 たとえば、データストアへの2つ以上の別々の接続が、それぞれ同じ文を、1個、2個、3個、… , n個準備した場合、TimesTenは最初に準備した文を覚えているため、n番目の準備はとても早く返されます。
例2-6 パラメータを使用した文の使用(複数の接続)
この例では、3つの接続に対して、パラメータを使用した同一のINSERT文を3つ準備します。 接続conn1に対して準備された1番目のINSERTは、conn2接続およびconn3接続で共有されるため、pIns2および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 (?,?)");
|
注意: 結合順序、索引、ロックなど、すべてのチューニング・オプションが共有される文に適している必要があります。 また、準備された文が一時表を参照する場合、この文は1つの接続内でのみ共有されます。 |
この項では、SQL文またはPL/SQLでの重複パラメータのバインドにおける考慮事項を説明します。
SQL文における重複パラメータのバインドには、次の2つのモードがサポートされています。
Oracleモードでは、同じパラメータ名の複数のインスタンスは別のパラメータとして認識されます。
以前のリリースからの従来型のTimesTenモードでは、同じパラメータ名の複数のインスタンスは、同じパラメータが複数回出現しているとみなされます。
DuplicateBindModeの一般的な接続属性を使用して、必要なモードを選択できます。 DuplicateBindMode=0(デフォルト)はOracleモードで、 DuplicateBindMode=1はTimesTenモードです。 これは一般的な接続属性のため、同じデータベースへの別々の同時接続には別の値を使用できます。 この属性の詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のDuplicateBindModeに関する項を参照してください。
この項の後半では、各モードの詳細を示します。次の問合せについて考えてみます。
SELECT * FROM employees WHERE employee_id < :a AND manager_id > :a AND salary < :b;
|
注意: この説明は、(PL/SQLなどを介してではなく)JDBCから直接発行されたSQL文にのみ該当します。 |
Oracleモードでは、SQL文内の、同じパラメータ名の複数のインスタンスは別のパラメータとして認識されます。 パラメータ位置番号を割り当てるとき、名前の重複は考慮せずに各パラメータの出現に番号が割り当てられます。 アプリケーションでは、最低でも、各パラメータ名の最初の出現に値がバインドされます。 そのパラメータ名が次回出現した場合、アプリケーションでは次のいずれかが実行されます。
その出現に対して別の値をバインドします。
そのパラメータの出現をバインドしないままにします。この場合は、最初の出現と同じ値をとります。
どちらの場合でも、それぞれの出現は、異なるパラメータ位置番号を持っています。
前述のSQL文で、aの2度目の出現に対して別の値を使用するには、次のようにします。
pstmt.setXXX(1, ...); /* first occurrence of :a */ pstmt.setXXX(2, ...); /* second occurrence of :a */ pstmt.setXXX(3, ...); /* occurrence of :b */
両方のaの出現に対して同じ値を使用するには、次のようにします。
pstmt.setXXX(1, ...); /* both occurrences of :a */ pstmt.setXXX(3, ...); /* occurrence of :b */
いずれの場合も、パラメータbは位置3にあるとみなされます。
TimesTenモードでは、重複パラメータを含むSQL文は、異なるパラメータ名のみが別のパラメータとみなされるように解析されます。 アプリケーションでは一意のパラメータごとにのみ1つの値がバインドされ、一意でないパラメータはバインドされません。
バインドは、パラメータ名が最初に出現した位置に基づいて行われます。 以降に出現するパラメータ名は、同じ値にバインドされ、パラメータ位置番号は与えられません。
前述のSQL文では、aの2つの出現が単一のパラメータとみなされるため、別々にバインドすることはできません。
pstmt.setXXX(1, ...); /* both occurrences of :a */ pstmt.setXXX(2, ...); /* occurrence of :b */
TimesTenモードでは、パラメータbは位置3ではなく、位置2にあるとみなされることに注意してください。
前述の説明は、PL/SQL内では該当しません。 かわりに、PL/SQLセマンティックを適用します。これによって、一意のパラメータごとに1つの値をバインドします。たとえば、次のブロックをコールするアプリケーションは、:aに対応するパラメータを1つのみバインドします。
DECLARE x NUMBER; y NUMBER; BEGIN x:=:a; y:=:a; END;
次のブロックをコールするアプリケーションも、1つのパラメータのみバインドします。
BEGIN INSERT INTO tab1 VALUES(:a, :a); END
次のブロックをコールするアプリケーションは、2つのパラメータ(パラメータ#1に:a、パラメータ#2に:b)をバインドします。各INSERT文の2番目のパラメータは、最初のINSERT文の1番目のパラメータと同じ値をとります。
BEGIN INSERT INTO tab1 VALUES(:a, :a); INSERT INTO tab1 VALUES(:b, :a); END
この例では、INSERT文とSELECT文を準備し、INSERTを2回実行し、SELECTを実行し、返された結果セットを出力します。 ここでは示されていませんが、「データ・ストアへの接続」に示すとおり、接続はオープンである必要があります。 作業例については、level1デモを参照してください。 (デモについては、「TimesTen Javaのデモ」を参照してください。)
例2-7 完全な例: 準備と実行
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();
結果セットの使用についてのその他の情報は、「TimesTen結果セットの使用: ヒントおよび制限」を参照してください。
標準のODBC機能を拡張するTimesTen組込みプロシージャ、およびTimesTen組込みプロシージャに必要な権限については、『Oracle TimesTen In-Memory Databaseリファレンス』の組込みプロシージャに関する章を参照してください。 TimesTen組込みプロシージャは、CallableStatementインタフェースの機能を使用して実行できます。
次のようにして、組込みプロシージャを実行します。
CallableStatement.execute("{ Call procedure }")
組込みプロシージャを準備および実行するには、次の書式を使用します。
CallableStatement cStmt;
cStmt = conn.prepareCall("{ Call procedure }");
cStmt.execute();
結果セットを返す組込みプロシージャについては、ResultSetインタフェースのgetXXX()メソッドを使用して、データを取得できます。例2-9を参照してください。
この例では、ttCkptプロシージャをコールしてファジー・チェックポイントを開始します。
Connection conn;
CallableStatement cStmt;
.......
cStmt = conn.prepareCall("{ Call ttCkpt }");
cStmt.execute();
conn.commit(); // commit the transaction
ttCkpt組込みプロシージャには、ADMIN権限が必要なことに注意してください。 その他の情報については、『Oracle TimesTen In-Memory Databaseリファレンス』のttCkptに関する項を参照してください。
この例では、ttDataStoreStatusプロシージャをコールして、返される結果セットを出力します。
「TimesTen結果セットの使用: ヒントおよび制限」に示した例とは異なり、この例では、ResultSetオブジェクトでgetString()コールを使用して、バイナリであるContextフィールドを取得します。 これは、出力は処理で使用されるのではなく、表示されるためです。 Context値を表示しない場合は、getBytes()メソッドをかわりに使用すると、より高いパフォーマンスが得られます。
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();
|
注意: TimesTenでは、名前を使用してパラメータをCallableStatementオブジェクトに渡すことはできません。序数を使用してパラメータを設定する必要があります。SQLのエスケープ構文は使用できません。 |
「SQL文の準備および入力パラメータの設定」では、文を準備して、PreparedStatementメソッドを使用して入力パラメータを設定する方法を説明します。 TimesTenでは、OUTパラメータおよびIN OUTパラメータもサポートしています。これには次のとおり、PreparedStatementのかわりにjava.sql.CallableStatementを使用します。
メソッドregisterOutParameter()を使用して、OUTパラメータまたはIN OUTパラメータを登録し、パラメータの位置(文内の位置)およびデータ型を指定します。
これは、CallableStatementインタフェースで指定されている標準のメソッドです。
void registerOutParameter(int parameterIndex, int sqlType, int scale)
ただし、CHAR、VARCHAR、NCHAR、NVARCHAR、BINARYまたはVARBINARYデータ型に対してこの標準バージョンを使用すると、TimesTenでは可能なかぎり最大値を保持できるようにメモリーを割り当てます。 これは、ほとんどの場合で無駄になります。
そのかわりに、TimesTenの拡張インタフェースTimesTenCallableStatementを使用します。これには、最大データ長をバイト単位で指定できるregisterOutParameter()シグネチャが含まれます。
void registerOutParameter(int paramIndex, int sqlType, int ignore, //This parameter is ignored by TimesTen. int maxLength)
適切なCallableStatementメソッドsetXXX()を使用し、(XXXはデータ型を示します)IN OUTパラメータの入力値を設定します。 パラメータの位置およびデータの値を指定します。
適切なCallableStatementメソッドgetXXX()を使用して、OUTパラメータまたはIN OUTパラメータの出力値を取得し、パラメータの位置を指定します。
|
注意: TimesTenの場合:
|
例2-10 コール可能な文でのOUTパラメータの使用
この例は、コール可能な文でのOUTパラメータの使用方法を示します。 TimesTenCallableStatementインスタンスで、PL/SQLブロックは、新しい給与を計算し、整数で返すファンクションRAISESALをコールします。 Connectionインスタンスをconnとします。 (PL/SQLに関する情報は、『Oracle TimesTen In-Memory Database 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();
...
RETURNING INTO句(DML RETURNINGとも呼ばれる)をINSERT、UPDATEまたはDELETE文で使用すると、処理によって変更があった行から、指定した項目を返すことができます。 これにより、たとえば処理によって影響を受けたものを確認する場合などで、続くSELECT文および個々のラウンド・トリップが不要になります。
TimesTenでは、DML RETURNINGは、単一行の操作から項目を戻すのみです。 この句はOUTパラメータのリストに項目を戻します。
TimesTenPreparedStatement(標準PreparedStatementインタフェースの拡張)は、DML RETURNINGをサポートします。
TimesTenPreparedStatementメソッドregisterReturnParameter()を使用して、戻りパラメータを登録します。 registerOutParameter()メソッド(前述の「OUTパラメータおよびIN OUTパラメータの使用」を参照)と同様、このメソッドはデータの最大サイズ(バイト)をオプションとして指定することができるシグネチャを含みます。 CHAR、VARCHAR、NCHAR、NVARCHAR、BINARYまたはVARBINARYのデータ型の場合、これによって、TimesTenで可能なかぎり最大値を保持するようにメモリーを割り当てるという非効率が発生しないようにします。
void registerReturnParameter(int paramIndex, int sqlType, int maxSize)
TimesTenPreparedStatementメソッドgetReturnResultSet()を使用して戻りパラメータを取得し、ResultSetインスタンスを返します。
ただし、次の制限に注意してください。
getReturnResultSet()メソッドは2回以上実行しないでください。 実行すると、動作は不確定です。
getReturnResultSet()が返す結果セットは、ResultSetMetaDataをサポートしていません。
getReturnResultSet()が返す結果セットは、getCharacterStream()などのストリーミング・メソッドをサポートしていません。
DML RETURNINGをサポートするバッチはありません。
SQL構文およびTimesTenのRETURNING INTO句の制限については、『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』でINSERT、UPDATEおよびDELETEの説明の一部として説明されています。
DML RETURNINGに関する一般的な情報は、『Oracle Database PL/SQL言語リファレンス』のRETURNING INTO句に関する項を参照してください。
|
重要: TimesTenの準備済の文の実行後は、SQL警告を確認してください。 警告イベント中には、出力パラメータは定義されません。 エラーおよび警告に関する一般的な情報は、「エラー処理」を参照してください。 |
例2-11 DML RETURNING
この例では、TimesTenPreparedStatementインスタンスでDML RETURNINGを使用する方法を示しています。挿入された行の名前および経過時間を返します。
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);
}
}
}
REF CURSORはPL/SQLの1つの概念で、ここでREF CURSORはSQL結果セット上のカーソルのハンドルであり、PL/SQLとアプリケーションの間で受渡しすることができます。 TimesTenでは、PL/SQL内でカーソルをオープンできるため、そのREF CURSORをアプリケーションに渡して結果セットを処理することができます。
アプリケーションは、次のように、REF CURSORをOUTパラメータとして受け取ることができます。
型TimesTenTypes.CURSOR(TimesTen型拡張)としてREF CURSOR OUTパラメータを登録すると、REF CURSORのパラメータ位置(文内の位置)も指定されます。
TimesTenCallableStatementインタフェース(TimesTen JDBC拡張の1つ)で定義されたgetCursor()メソッドを使用してREF CURSORを取得し、REF CURSORのパラメータ位置を指定します。 getCursor()メソッドは他のgetXXX()メソッドと同様に使用され、ResultSetインスタンスを返します。
|
重要: PL/SQLとアプリケーションの間でのREF CURSORの受け渡しについて、TimesTenでは、PL/SQLからアプリケーションへのOUT REF CURSORのみをサポートし、単一のREF CURSORのみを返す文をサポートしています。 |
次の例でこれを説明します。
例2-12 REF CURSORの使用
この例は、コール可能な文でのREF CURSORの使用方法を示します。 PL/SQLブロックは、CallableStatementインスタンスでカーソルをオープンし、問合せを実行します。 TimesTenCallableStatementメソッドgetCursor()は、カーソルを戻すために使用され、そのカーソルは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();
...
TimesTenデータベース表の各行は、ROWIDと呼ばれる一意の識別子を持っています。 アプリケーションは、行のROWIDをROWID擬似列から取得できます。 ROWID値はバイナリまたは文字形式で表現され、12バイトのバイナリ形式および18バイトの文字形式で表されます
次のいずれかのResultSetメソッドを使用して、ROWIDを取得できます。
byte[] getBytes(int columnIndex)
String getString(int columnIndex)
Object getObject(int columnIndex)(Stringオブジェクトを返します)
次のいずれかのPreparedStatementメソッドを使用して、ROWIDを設定できます。
setBytes(int parameterIndex, byte[] x)
setString(int parameterIndex, String x)
setObject(int parameterIndex, Object x)(Stringオブジェクトをとります)
|
注意: getBytes()またはsetBytes()を、PL/SQLパラメータまたはパススルー・パラメータ(Oracle In-Memory Database Cacheの使用時にOracleへ渡されるパラメータ)になるROWIDパラメータに使用することはできません。 getString()およびsetString()を使用するか、または getObject()およびsetObject()をStringオブジェクトとともに使用してください。 |
たとえばWHERE句にある一重引用符で囲んだCHAR定数のように、SQL文にリテラルROWID値を指定できます。
使用方法およびライフサイクルなど、ROWIDおよびROWIDデータ型に関するその他の情報は『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のROWIDデータ型およびROWID仕様に関する項を参照してください。
|
注意: Oracle TimesTen In-Memory Databaseは、PL/SQL型UROWIDをサポートしません。 |
TimesTenデータ・ストアから複数のデータ行をフェッチすると、コミット読取り分離レベルが設定されたデータ・ストアに接続しているアプリケーションのパフォーマンスを向上させることができます。
プリフェッチされる行数を指定するには、次の手順を実行します。
StatementまたはResultSetメソッドsetFetchSize()をコールします。これらのメソッドは標準のJDBCコールですが、1回の実行で効果があるのは1つの文のみであるという制限があります。
TimesTenConnectionメソッドsetTtPrefetchCount()をコールします。 これによって、ある接続に対するすべての文で同じプリフェッチ設定が使用されるように、その接続レベルでのプリフェッチを確立するTimesTenの拡張が有効になります。
この項では、TimesTenで実装されている接続レベルのプリフェッチについて説明します。
|
注意: 直接リンクされたアプリケーションでのみ、TimesTenのプリフェッチ・カウント拡張を使用できます。 |
プリフェッチ数を0(ゼロ)に設定すると、TimesTenは、データ・ストアに設定した分離レベルに応じて、デフォルトの値を使用します。コミット読取り分離モードでは、デフォルトのプリフェッチの値は5です。 シリアライズ可能分離モードでは、デフォルトのプリフェッチの値は128です。 デフォルトのプリフェッチの値はほとんどのアプリケーションに最適な設定です。一般的に、値を高く設定すると、リソースの使用量がわずかに増加しますが、大きい結果セットに対するパフォーマンスは向上する可能性があります。
プリフェッチを無効にするには、プリフェッチ数を1に設定します。
TimesTenConnectionメソッドgetTtPrefetchCount()をコールして、現行のプリフェッチ値を確認します。
例2-13 プリフェッチ・カウントの設定
次のコードでは、setTtPrefetchCount()コールを使用して、プリフェッチ数を10に設定し、getTtPrefetchCount()コールを使用して、カウント変数のプリフェッチ数を返します。
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();
TimesTenには、SQL文の実行時間を制限する2通りの方法があり、execute()、executeBatch()、executeQuery()、executeUpdate()またはnext()のコールで使用できます。
前者はタイムアウトを設定します。タイムアウト時間に達すると、文の実行が停止し、エラーがスローされます。 後者はしきい値を設定します。しきい値に達すると、SNMPトラップがスローされますが、実行は継続されます。
TimesTenでは、SqlQueryTimeoutDSN属性を設定して、すべての接続(つまり、すべての文)に対してタイムアウト期間(秒)を指定できます。 DSN仕様にSqlQueryTimeoutを設定すると、その値が、今後のデータ・ストアへのすべての接続のデフォルト値となります。 名前に反して、このタイムアウト値は問合せだけでなく、実行可能な任意のSQL文に適用されます
特定の文について、StatementメソッドsetQueryTimeout()をコールすることで、SqlQueryTimeout設定を上書きすることができます。
問合せのタイムアウト制限は、SQL文がアクティブに実行されている場合のみ有効です。 処理のコミット中またはロールバック中にタイムアウトは発生しません。 多数のUPDATE、DELETEまたはINSERT文を実行するトランザクションでは、コミットまたはロールバックが完了するまでに時間がかかる場合があります。その間、タイムアウト値は無視されます。
|
注意: ロック待機とSqlQueryTimeoutの両方が指定されている場合、まず、2つの値の小さい方の値によってタイムアウトが発生します。 ロック・タイムアウトについては、『Oracle TimesTen In-Memory Databaseリファレンス』を参照してください。ttLockWait組込みプロシージャに関する情報は、ttLockWaitに関する項を参照してください。LockWait接続属性に関する情報はLockWaitに関する項を参照してください。 または、『Oracle TimesTen In-Memory Databaseトラブルシューティング・プロシージャ・ガイド』のタイムアウトおよびデッドロックの確認に関する項も参照してください。 |
SQL文の実行時間が、指定した期間(秒)を超えたときに、サポート・ログに警告を書き込み、SNMPトラップをスローするように、TimesTenを構成できます。 実行は継続され、しきい値の影響は受けません。
SNMPトラップの名前は、ttQueryThresholdWarnTrapです。 SNMPトラップの構成に関する情報は、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』を参照してください。
名前に反して、このしきい値は問合せだけでなく、SQL文を実行する任意のJDBCコールに適用されます
デフォルトでは、アプリケーションはしきい値をQueryThreshold接続属性設定から取得します。 データ・ストアの接続URLにQueryThreshold属性を含めることでJDBC Connectionオブジェクトのしきい値を上書きすることができます。 たとえば、myDSNデータ・ストアのQueryThresholdを5秒に設定するには、次のようにします。
jdbc:timesten:direct:dsn=myDSN;QueryThreshold=5
TimesTenStatementオブジェクトのsetQueryTimeThreshold()メソッドを使用して、しきい値を設定することもできます。 これは、接続属性設定およびConnection オブジェクト設定を上書きします。
TimesTenStatementオブジェクトのgetQueryTimeThreshold()メソッドを使用して現行のしきい値を取得できます。
JDBCでSQLを使用する際は、Javaエスケープ構文に特に注意します。 UNISTRなどのSQL関数は、バックスラッシュ文字(\)を使用します。バックスラッシュ文字は、エスケープする必要があります。たとえば、Javaアプリケーションで次のSQL構文を使用すると、意図した結果にならない場合があります。
INSERT INTO table1 SELECT UNISTR('\00E4') FROM dual;
バックスラッシュ文字を次のようにエスケープします。
INSERT INTO table1 SELECT UNISTR('\\00E4') FROM dual;
TimesTenでは、FLUSH CACHE GROUP、LOAD CACHE GROUP、REFRESH CACHE GROUPまたはUNLOAD CACHE GROUP文を実行すると、StatementメソッドgetUpdateCount()が、フラッシュ、ロード、リフレッシュ、アンロードされたキャッシュ・インスタンスの数を返します。
関連する情報について、『Oracle In-Memory Database Cacheユーザーズ・ガイド』の操作で影響を受けたキャッシュ・インスタンスの数の判別に関する項を参照してください。
この項では、自動コミットおよび手動コミット、またはロールバックについて説明します。JDBC Connectionオブジェクトをmyconnとします。
TimesTen接続は、デフォルトで自動コミットが有効になっていますが、無効にすることをお薦めします。 ConnectionメソッドsetAutoCommit()を使用して、自動コミットの有効/無効を切り替えることができます。
自動コミットを無効化する方法
myconn.setAutoCommit(false); // Report any SQLWarnings on the connection // See "Reporting errors and warnings"
自動コミットが無効化された場合、Connectionメソッドcommit()を使用して手動でトランザクションをコミットするか、またはrollback()メソッドを使用して変更をロールバックする必要があります。次に例を示します。
myconn.commit();
または
myconn.rollback();
|
注意: 一部のUNIXプラットフォームでは、THREADS_FLAG変数を設定する必要があります。『Oracle TimesTen In-Memory Databaseインストレーション・ガイド』のTHREADS_FLAG変数の設定に関する項を参照してください。 |
level4デモでは、複数スレッドの使用について説明します。 「TimesTen Javaのデモ」を参照してください。
アプリケーションでデータ・ストアへの直接ドライバ接続が行われる場合、TimesTenの機能はアプリケーションとスタック領域を共有します。マルチ・スレッド環境では、各スレッドに割り当てられたスタックのオーバーランを避けることが重要です。 これは、オーバーランによって、予想できないデバッグ困難な結果になる可能性があるためです。TimesTenのコールで消費されるスタック領域の量は、使用するSQL機能によって異なります。ほとんどのアプリケーションでは、32-bitシステムで16KB、64-bitシステムで34KBから72KBのスレッド・スタック領域を設定する必要があります。
スレッドごとに割り当てられるスタック領域の量は、スレッドの作成時にオペレーティング・システムによって指定されます。 Windowsでは、TimesTenデバッグ・ドライバを使用して、Visual C++デバッグCライブラリにアプリケーションをリンクすると、スタック・プローブを有効にすることができます。 これにより、スレッドで割り当てられた量を超えてそのスタックを増やそうとすると、識別可能な例外が発生します。
|
注意: マルチ・スレッド・アプリケーションでは、同じデータ・ストアへの様々な接続ハンドルにリクエストを発行するスレッドで、それ自身とのロック競合が発生する可能性があります。 TimesTenでは、ロック・タイムアウトを使用してこれらの競合を解消します。 |
TimesTenには、データベース・オブジェクト(表、ビュー、マテリアライズド・ビュー、順序など)をオブジェクトレベルで分解することによる、データベース・アクセス制御の機能があります。 TimesTenアクセス制御の概要については、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のアクセス制御の管理に関する項を参照してください。
この項では、SQL操作、データ・ストア接続およびJMS/XLAに関連するアクセス制御について説明します。
このマニュアルで説明されている、または例として使用されている問合せ、SQL DML文またはSQL DDL文では、ユーザーは、文の実行に適切な権限を持っていると想定されています。 たとえば、表へのSELECT文には、その表の所有権、表に付与されたSELECT権限またはSELECT ANY TABLEシステム権限が必要です。 同様に、DML文では、表の所有権、表に付与された適用可能なDML権限(UPDATEなど)または適用可能なANY TABLE権限(UPDATE ANY TABLEなど)が必要です。
DDL文では、CREATE TABLEは、ユーザーのスキーマのCREATE TABLE権限または他のスキーマのCREATE ANY TABLE権限を必要とします。 ALTER TABLEは、所有権またはALTER ANY TABLEシステム権限を必要とします。 DROP TABLEは、所有権またはDROP ANY TABLEシステム権限を必要とします。 オブジェクトレベルのALTER権限またはDROP権限はありません。
アクセス制御権限のリストおよび与えられたSQL文に必要な権限については、『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のSQL文に関する項を参照してください。
権限は、SQL文GRANTによって付与され、REVOKEによって取り消されます。 一部の権限は、すべてのユーザーがメンバーになっているPUBLICロールによって自動的にすべてのユーザーに付与されます。 このロールに関する情報は、『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のPUBLICロールに関する項を参照してください。
また、アクセス制御は、このマニュアルで説明している次の内容に関係します。
データ・ストアへの接続。 「接続のアクセス制御」を参照してください。
接続属性の設定。 「データ・ストアの接続URLの作成および接続属性の指定」を参照してください。
JMS/XLAの構成および管理。 「アクセス制御がXLAに与える影響」を参照してください。
|
注意:
|
この項では、TimesTen Javaアプリケーションで発生するエラーの検出、識別および処理について説明します。
TimesTenにより返されるエラーのリストおよびエラーが発生した場合の対処方法については、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』の警告およびエラーに関する章を参照してください。
この項の内容は次のとおりです。
TimesTenでは、致命的エラー、致命的でないエラーまたは警告が返される場合があります。
致命的なエラーが発生すると、リカバリするまでデータ・ストアにアクセスできなくなります。致命的エラーが発生すると、すべてのデータ・ストアの接続を切断する必要があります。それ以後の処理は完了されません。致命的エラーは、TimesTenのエラー・コード846および994で示されます。 これらのエラーの処理は、標準的なエラーの処理とは異なります。具体的には、コードによってトランザクションをロールバックし、データ・ストアからの接続を切断する必要があります。
致命的エラーが発生した場合、TimesTenは、次の完全なクリーンアップおよびリカバリ・プロシージャを実行します。
データ・ストアに対するすべての接続を無効し、新しいメモリー・セグメントを割り当て、アプリケーションを切断します。
その後の最初の初期接続時に、チェックポイント・ファイルおよびトランザクション・ログ・ファイルからデータ・ストアをリカバリします。
リカバリされたデータ・ストアには、永続コミットされたすべてのトランザクションの状態が反映されます。 また、非永続的にコミットされた一部のトランザクションも反映されます。
コミットされていないトランザクションまたはロールバックされたトランザクションは反映されません。
チェックポイントまたはトランザクション・ログ・ファイルがなく、AutoCreate属性が設定されている場合、TimesTenは空のデータ・ストアを作成します。
致命的ではないエラーには、一意制約に違反しているINSERTなどの単純なエラーが含まれます。また、一部のアプリケーション障害およびプロセス障害も、致命的ではないエラーに含まれます。
TimesTenでは、致命的ではないエラーも、通常のエラー処理プロセスによって返されます。 また、エラーの検出および識別はアプリケーションで行う必要があります。
致命的ではないエラーによってデータ・ストアに影響が出た場合、エラーが返されることがあり、アプリケーションで適切に対処する必要があります。 プロセスの障害などの場合はエラーが返されないため、失敗したプロセスのトランザクションが自動的にロールバックされます。
アプリケーションでは、その処理を変更するか、または障害が発生した1つ以上のトランザクションをロールバックすることによって、致命的ではないエラーに対処できます。 詳細は、「失敗したトランザクションのロールバック」を参照してください。
|
注意: ResultSet、Statement、PreparedStatement、CallableStatementまたはConnection操作でデータ・ストアにエラーが発生する場合は、そのオブジェクトに対してclose()メソッドをコールすることをお薦めします。 |
コールのたびに返される可能性があるすべてのエラーと警告を確認してレポートする必要があります。これによって、開発およびデバッグの時間および労力が大幅に削減されます。データ・ストアのアクセス時にエラーが1つ以上ある場合は、SQLExceptionオブジェクトが発生し、警告メッセージが1つ以上ある場合は、SQLWarningオブジェクトが発生します。 1回のコールで複数のエラーまたは警告(あるいはその両方)が返されることがあるため、アプリケーションでは、返されたSQLExceptionオブジェクトまたはSQLWarningオブジェクトにすべてのエラーまたは警告をレポートする必要があります。
SQLExceptionオブジェクトまたはSQLWarningオブジェクトのリンク連鎖では、複数のエラーまたは警告が返されます。 例2-14および例2-15に、返されたSQLExceptionオブジェクトとSQLWarningオブジェクトのリストで繰り返し実行し、それぞれにすべてのエラーと警告をレポートする方法を示します。
例2-14 例外の表示
このメソッドは、リンクされたSQLExceptionオブジェクトのすべての例外の内容を表示します。
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;
}
例2-15 警告の表示
このメソッドは、リンクされたSQLWarningオブジェクトのすべての例外の内容を表示します。
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;
}
特定のSQLの状態またはTimesTenエラー・コードに対応する必要がある場合があります。 SQLExceptionメソッドgetSQLState()を使用してSQL99 SQL状態エラー文字列を返したり、getErrorCode()を使用してTimesTenエラー・コードを返すことができます。例2-16を参照してください。
エラーの情報は、『Oracle TimesTen In-Memory Database JDBC Extensions Java API Reference』のTimesTenVendorCodeに関する項も参照してください。
例2-16 エラーの検出
TimesTenのデモでは、デモ・スキーマをロードしてから実行する必要があります。 ODBCエラーS0002およびTimesTenエラー907が検出されると、次のcatch文により、appuserがロードまたは更新されていないというアラートがユーザーに発行されます。
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.");
}
TimesTenVendorCodeインタフェースを使用すると、番号ではなく名前によって、エラーを検出できます。
次の例を考えてみます。
ex.getErrorCode() == com.timesten.jdbc.TimesTenVendorCode.TT_ERR_KEYEXISTS
これは同等です。
ex.getErrorCode() == 907
デッドロックまたはタイムアウトの状態からリカバリする場合などでは、次の例のように、Connection.rollback()メソッドを使用して明示的にトランザクションをロールバックする場合があります。
例2-17 トランザクションのロールバック
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();
}
SYS.MONITOR表のXACT_ROLLBACKS列は、ロールバックされたトランザクションの数を示します。
トランザクションのロールバックによってリソースが消費され、結果的にトランザクション全体が無駄になってしまいます。 不要なロールバックを回避するには、可能なかぎり、競合が発生しないようにアプリケーションを設計し、送信前にアプリケーションまたは入力データをチェックしてエラーを検出する必要があります。
|
注意: アクティブなトランザクションの途中でアプリケーションに障害が発生した場合、このトランザクションは自動的にロールバックされます。 |
自動クライアント・フェイルオーバーは、TimesTenノードの障害により代替ノードにフェイルオーバー(転送)する結果になったときに、高可用性シナリオで使用されるもので、アプリケーションを新しいノードに自動的に再接続します。 TimesTenは自動クライアント・フェイルオーバーが発生したときに、アプリケーションに警告を渡す機能を提供しているため、アプリケーションは適切な処理を行うことができます。
この項では、自動クライアント・フェイルオーバーに関連するTimesTen JDBC拡張について説明します。次の内容について説明します。
|
注意: 自動クライアント・フェイルオーバーは、クライアント/サーバー・モードでのみ使用します。 ここで説明する機能は、直接接続には使用できません。 |
自動クライアント・フェイルオーバーはOracle Clusterwareを補足するものですが、この2つの機能に依存関係はありません。
関連する情報は、『Oracle TimesTen In-Memory Database C開発者ガイド』の自動クライアント・フェイルオーバーに関する項も参照してください。 Oracle Clusterwareに関する情報は、『Oracle TimesTen In-Memory Database TimesTen to TimesTen開発者および管理者ガイド』のOracle Clusterwareを使用してのアクティブ・スタンバイ・ペアの管理に関する項を参照してください。
この項では、クライアント・フェイルオーバーに関する一般的なTimesTen JDBC機能、およびプールされた接続に特に関連する機能について説明します。
自動クライアント・フェイルオーバーに対するTimesTen JDBCサポートには、フェイルオーバーを検出するために次の2つのメカニズムがあります。
SQL例外による同期検出。 自動クライアント・フェイルオーバーが発生すると、障害が発生した接続で作成されたJDBCオブジェクト(文、準備済の文、コール可能な文、結果セットなど)は使用できなくなります。 そのようなオブジェクトにアプリケーションがアクセスを試みた場合、Java SQL例外がスローされます。 その例外のSQL状態およびエラーコードを確認することで、例外が、フェイルオーバーの状況の結果であるかどうかを判断できます。
イベント・リスナーによる非同期検出。 アプリケーションは、ユーザーが定義したクライアント・フェイルオーバー・イベント・リスナーを登録できます。これには、フェイルオーバーのプロセスで発生した各イベントが通知されます。
TimesTen JDBCは、パッケージcom.timesten.jdbcで自動クライアント・フェイルオーバーをサポートするために次の機能を提供します。
ClientFailoverEventクラス。 このクラスは、クライアント・フェイルオーバー中に発生したイベント(開始、終了、中止または再試行)を表現するために使用されます。
ClientFailoverEventListenerインタフェース。 クライアント・フェイルオーバー・イベントに関係するアプリケーションには、このインタフェースを実装したクラスを含める必要があります。これは、クライアント・フェイルオーバーをリスニングするためのメカニズムです。 実行時、アプリケーションは、TimesTen接続(すぐ次の項目を参照)で、ClientFailoverEventListenerインスタンスを登録する必要があります。
TimesTenConnectionインタフェースの新しいメソッド。 このインタフェースはメソッドaddConnectionEventListener()およびremoveConnectionEventListener()を指定し、それぞれクライアント・フェイルオーバー・イベント・リスナーの登録または削除を行います。
TimesTenVendorCodeインタフェースの新しい定数TT_ERR_FAILOVERINVALIDATION。 これは、イベントがフェイルオーバー・イベントであると確認できます。
TimesTenでは、プールされた接続(javax.sql.PooledConnection)または接続プール・データソース(javax.sql.ConnectionPoolDataSource)を使用するアプリケーションで、前述の同期メカニズムを使用し、障害が発生した接続で失効したオブジェクトを処理することをお薦めします。 プールされた接続はJava EEアプリケーション・サーバーで管理するため、プールされた接続でのイベントをアプリケーションでリスニングすることはできません。 またアプリケーション・サーバーでは、TimesTenの拡張のClientFailoverEventListenerインスタンスを実装または登録しません。
ファイルオーバー時に、障害が発生した接続に作成されたオブジェクトをアプリケーションで使用しようとすると、JDBCはSQL例外をスローします。 ベンダー固有の例外コードは、TimesTenVendorCode.TT_ERR_FAILOVERINVALIDATIONに設定されます。
このメカニズムによるフェイルオーバーの検出は、同期検出として示されます。 次の例でこれを説明します。
例2-18 自動クライアント・フェイルオーバーの同期検出
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.
}
}
非同期のフェイルオーバーの検出には、アプリケーションでクライアント・フェイルオーバー・イベント・リスナーを実装し、そのインスタンスをTimesTen接続に登録する必要があります。 この項では、その手順を説明します。
TimesTen JDBCは、(次のメソッドで強調されている)イベントのリスニングに使用するcom.timesten.jdbc.ClientFailoverEventListenerインタフェースを提供します。
void notify(ClientFailoverEvent event)
非同期フェイルオーバー検出を使用するには、このインタフェースを実装するクラスを作成した後、TimesTen接続の実行時に、クラスのインスタンスを登録する必要があります(概要が説明されています)。
フェイルオーバー・イベントが発生すると、TimesTenは、登録したリスナー・インスタンスのnotify()メソッドをコールして、イベントに関する情報を確認することができるClientFailoverEventインスタンスを提供します。
次の例は、ClientFailoverEventListener実装の基本的な形式を示しています。
例2-19 自動クライアント・フェイルオーバーの非同期検出
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;
}
}
}
event.getTheFailoverType()コールは、ネストされたクラスClientFailoverEvent.FailoverTypeのインスタンスを返します。これは列挙型です。 TimesTenがサポートする値は、接続のフェイルオーバーを示すTT_FO_CONNECTIONのみです。
event.getTheFailoverEvent()コールは、ネストされたクラスClientFailoverEvent.FailoverEventのインスタンスを返します。これは列挙型で、その値は次のいずれかにあります。
BEGIN: クライアント・フェイルオーバーが開始した場合。
END: クライアント・フェイルオーバーが正常に完了した場合。
ERROR: クライアント・フェイルオーバーに失敗したが、再試行する場合。
ABORT: クライアント・フェイルオーバーが中断した場合。
実行時、フェイル・オーバー・イベント・リスナー・クラスのインスタンスを、TimesTen接続オブジェクトに登録する必要があります。そうすることで、フェイルオーバー・イベントに必要な、そのリスナー・クラスのnotify()メソッドをコールすることができるようになります。
これを実行するため、TimesTenConnectionクラスには、次のメソッドがあります。
void addConnectionEventListener (ClientFailoverEventListener listener)
リスナー・クラスのインスタンスを作成した後、このメソッドを使用して登録します。 次の例では、接続を確立した後にリスナーを登録します。 theDsnはTimesTenクライアント/サーバー・データ・ストアのJDBC URLとし、theCFListenerはフェイルオーバー・イベント・リスナー・クラスのインスタンスとします。
例2-20 クライアント・フェイルオーバー・リスナーの登録
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();
}
TimesTenConnectionインタフェースは次のメソッド定義して、フェイルオーバー・イベント・リスナーを登録解除します。
void removeConnectionEventListener (ClientFailoverEventListener listener)
リスナー・インスタンスを登録解除する場合は、このメソッドを使用します。