エラー処理
TimesTen Javaアプリケーションで発生するエラーの確認、識別および対処の方法を示します。
TimesTenによって返されるエラーのリスト、およびエラーが発生した場合の対処方法については、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』のエラーおよび警告を参照してください。
この項の内容は次のとおりです。
エラーおよび警告のレベル
操作が完全には成功しない場合、TimesTenは、致命的なエラー、致命的ではないエラーまたは警告を返すことができます。
次の項では、エラーまたは警告の状況とそれらの処理方法について説明します。
致命的なエラー
致命的なエラーが発生すると、リカバリするまでデータベースにアクセスできなくなります。致命的なエラーが発生すると、すべてのデータベースの接続を切断する必要があります。それ以後の処理は完了されません。
致命的エラーは、TimesTenのエラー・コード846および994で示されます。これらのエラーの処理は、標準的なエラーの処理とは異なります。特に、コードで現行のトランザクションをロールバックし、サーバーでのメモリー不足状態を避けるためにデータベースからの接続を切断する必要があります。古いTimesTenインスタンスの共有メモリーは、エラー発生時にアクティブだったすべての接続が切断されるまで解放されません。古いTimesTenインスタンスに引き続き接続されている非アクティブなアプリケーションは、手動で終了する必要があることがあります。
致命的エラーが発生した場合、TimesTenは、次の完全なクリーンアップおよびリカバリ・プロシージャを実行します。
-
データベースに対するすべての接続を無効し、新しいメモリー・セグメントを割り当て、アプリケーションを切断します。
-
その後の最初の初期接続時に、チェックポイント・ファイルおよびトランザクション・ログ・ファイルからデータベースをリカバリします。
-
リカバリされたデータベースには、永続コミットされたすべてのトランザクションの状態が反映され、非永続的にコミットされた一部のトランザクションも反映されます。
-
コミットされていないトランザクションまたはロールバックされたトランザクションは反映されません。
-
致命的ではないエラー
致命的ではないエラーには、一意制約に違反しているINSERT
文などのエラーが含まれます。また、一部のアプリケーション障害およびプロセス障害も、致命的ではないエラーに含まれます。
TimesTenは、通常のエラー処理プロセスを介して致命的ではないエラーを返します。アプリケーションは、エラーがないか確認し、これらを適切に処理する必要があります。
致命的ではないエラーによってデータベースに影響が出た場合、エラーが返されることがあり、アプリケーションで適切に対処する必要があります。
アプリケーションでは、そのアクションを変更するか、場合によっては、障害が発生した1つ以上のトランザクションをロールバックすることで、致命的ではないエラーに対処できます。詳細は、「失敗したトランザクションのロールバック」を参照してください。
後述の「エラーおよび警告のレポート」も参照してください。
ノート:
ResultSet
、Statement
、PreparedStatement
、CallableStatement
またはConnection
操作でデータベースにエラーが発生する場合は、そのオブジェクトに対してclose()
メソッドをコールすることをお薦めします。
警告
TimesTenは、予期しないことが発生してもあまり深刻でないものとみなされた場合に警告を返します。
たとえば、次の状況が発生した場合、TimesTenは警告を発行します。
-
チェックポイントの障害
-
非推奨のTimesTen機能の使用
-
一部のデータの切捨て
-
接続時のリカバリ処理の実行
-
レプリケーションがRETURN RECEIPTの場合のタイムアウト
アプリケーションの問題を示すことができるように、警告をチェックするコードを常に含める必要があります。
次に示す「エラーおよび警告のレポート」も参照してください。
エラーおよび警告のレポート
アプリケーションは、コールごとに返される可能性があるすべてのエラーと警告を確認してレポートする必要があります。これによって、開発およびデバッグの時間および労力が大幅に削減されます。
データベースのアクセス時にエラーが1つ以上ある場合は、SQLException
オブジェクトが発生し、警告メッセージが1つ以上ある場合は、SQLWarning
オブジェクトが発生します。1回のコールで複数のエラーまたは警告(あるいはその両方)が返されることがあるため、アプリケーションでは、返されたSQLException
オブジェクトまたはSQLWarning
オブジェクトにすべてのエラーまたは警告をレポートする必要があります。
SQLException
オブジェクトまたはSQLWarning
オブジェクトのリンク連鎖では、複数のエラーまたは警告が返されます。次の例に、返されたSQLException
オブジェクトとSQLWarning
オブジェクトのリストで繰り返し実行し、それぞれにすべてのエラーと警告をレポートする方法を示します。
次のメソッドは、リンクされた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;
}
次のこのメソッドは、リンクされた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()
を使用するとSQL状態が返され、getErrorCode()
メソッドを使用するとTimesTenエラー・コードが返されます。
Oracle TimesTen In-Memory Database JDBC拡張機能Java APIリファレンスのTimesTenVendorCode
の項目も参照してください。
TimesTen Classicクイック・スタート・サンプル・アプリケーションでは、サンプルを実行する前にスキーマをロードする必要があります。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()
を使用してトランザクションを明示的にロールバックする必要があります。
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();
} catch (SQLException ex) {
reportSQLExceptions(ex);
}
トランザクションのロールバックによってリソースが消費され、結果的にトランザクション全体が無駄になってしまいます。不要なロールバックを回避するには、競合が発生しないようにアプリケーションを設計し、送信前にアプリケーションまたは入力データをチェックしてエラーを検出する必要があります。
ノート:
アクティブなトランザクションの途中でアプリケーションが終了、クラッシュまたは切断した場合、TimesTenはトランザクションを自動的にロールバックします。
一時エラー後の再試行(JDBC)
TimesTenではほとんどの一時エラーは自動的に解決されますが(これはTimesTen Scaleoutにおいてとりわけ重要です)、アプリケーションで次のSQLSTATE
値が検出された場合は、現在のトランザクションを再試行することをお薦めします。
-
TT005
: Transient transaction failure due to unavailability of resource.Roll back the transaction and try it again.
ノート:
再試行が妥当かどうかを判断する前に、エラー・スタック全体でこれらのエラー・タイプを返すエラーを検索してください。
これはSQLException
クラスのgetSQLState()
メソッドによって返されるもので、次のいずれかのJDBC型のメソッド・コールにより発生する場合があります。
-
Connection
-
Statement
-
PreparedStatement
-
CallableStatement
-
ResultSet
-
Connection
-
Statement
-
PreparedStatement
-
CallableStatement
-
ResultSet
次に例を示します。
// Database connection object
Connection dbConn;
// Open the connection to the database
...
// Disable auto-commit
dbConn.setAutoCommit( false );
...
// Prepre the SQL statements
PreparedStatement stmtQuery = dbConn.prepare("SELECT ...");
PreparedStatement stmtUpdate = dbConn.prepare("UPDATE ...");
...
// Set max retries for transaction to 5
int retriesLeft = 5;
// Records outcome
boolean success = false;
// Excute transaction with retries until success or retries exhausted
while ( retriesLeft > 0 )
{
try {
// First execute the query
// Set input values
stmtQuery.setInt(1, ...);
stmtQuery.setString(2, ...);
// Execute and process results
ResultSet rs = stmtQuery.executeQuery();
while ( rs.next() )
{
int val1 = rs.getInt(1);
String val2 = rs.getString(2);
...
}
rs.close();
rs = null;
// Now excute the update
// Set input values
stmtUpdate.setInt(1,...);
stmtUpdate.setString(2,...);
// Execute and check number of rows affected
int updCount = stmtUpdate.executeUpdate();
if ( updCount < 1 )
{
...
}
// And finally commit
dbConn.commit();
// We are done
success = true;
break;
} catch ( SQLException sqe ) {
if ( sqe.getSQLState().equals("TT005") ) // grid transient error
{
// decrement retry count
retriesLeft--;
// and rollback the transaction ready for retry
try {
dbConn.rollback();
} catch ( SQLException sqer ) {
// This is a fatal error so handle accordingly
}
}
else
{
// handle other kinds of error
...
}
}
} // end of retry loop
if ( ! success )
{
// Handle the failure
...
}
ノート:
「フェイルオーバーの遅延および再試行の設定」の例では、一時エラーの場合の再試行方法も示しています。