ヘッダーをスキップ
Oracle® Database JDBC開発者ガイド
12cリリース1 (12.1)
B71308-02
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

E トラブルシューティング

この付録では、Java Database Connectivity(JDBC)アプリケーションまたはアプレットのトラブルシューティングについて説明します。内容は次のとおりです。

一般的な問題

この項では、Oracle JDBCドライバの使用中に発生する可能性のある、一般的な問題について説明します。たとえば、次の問題があります。

OUTまたはIN/OUT変数として定義されたCHAR列に対するメモリー消費

PL/SQLでは、CHARまたはVARCHAR2列がOUTまたはIN/OUT変数として定義される場合、ドライバは32512文字のCHAR配列を割り当てます。このため、メモリー消費に関する問題が生じます。JDBC Thinドライバは、VARCHAR2出力型を使用する場合、メモリーを割り当てません。ただし、JDBC OCIドライバは、CHARVARCHAR2の両方の型に対してメモリーを割り当てます。このため、OCIドライバのCPU負荷はThinドライバより高くなります。

以前のリリースでは、この問題はStatement.setMaxFieldSizeメソッドをコールすることで解決できました。これよりもよい解決方法は、OracleCallableStatement.registerOutParameterを使用することです。すべてのCHARまたはVARCHAR2列で、必ずregisterOutParameter(int paramIndex, int sqlType, int scale, int maxLength)をコールすることをお薦めします。このメソッドは、oracle.jdbc.OracleCallableStatementに定義されています。4つ目の引数maxLengthを使用して、メモリー消費を制限します。このパラメータは、この列の格納に必要な文字数をドライバに指示します。文字配列に列データを保持できない場合、列は切り捨てられます。3つ目の引数scaleは、ドライバでは無視されます。

メモリー・リークおよびカーソルの不足

カーソルまたはメモリーが不足しているというメッセージを受け取った場合は、すべてのStatementおよびResultSetオブジェクトを明示的にクローズしてください。Oracle JDBCドライバには、ファイナライザ・メソッドがありません。クリーン・アップ・ルーチンは、ResultSetおよびStatementクラスのcloseメソッドで実行されます。結果セットおよび文オブジェクトを明示的にクローズしておかないと、重大なメモリー・リークが発生します。また、データベースのカーソルが不足します。文をクローズすると、データベース内の対応するカーソルが解放されます。

同様に、サーバー側でのリークおよびカーソル不足を避けるには、Connectionオブジェクトも明示的にクローズしておく必要があります。接続をクローズすると、その接続に関連付けられたオープン中の文オブジェクトが、JDBCドライバによってクローズされ、サーバー側のカーソルが解放されます。

1プロセスで17以上のOCI接続のオープン数について

必ずしも1プロセスで約17以上のJDBC-OCI接続をオープンできないことがあります。サーバー上のプロセス数が初期化ファイルに指定されている制限を超えたためか、またはプロセスごとのファイル記述子の制限を超えた可能性があります。1つのJDBC-OCI接続で、複数のファイル記述子が使用される(3から4個のファイル記述子が使用されます)ことがあります。

サーバーで17以上のプロセスを使用可能にしている場合は、プロセスごとのファイル記述子の制限が原因である可能性があります。この制限数を増やすと、解決する可能性があります。

Statement.cancelの使用

JDBC標準メソッドStatement.cancelは、データベースにメッセージを送信することにより、SQL文の実行を正常に停止させます。データベースは、これに応答して実行を停止し、エラー・メッセージを返します。Statement.executeをコールしたJavaスレッドはサーバーで待機し、他のスレッドからのStatement.cancelメソッドのコールにより起動された返答のエラー・メッセージを受信した場合にのみ実行を続けます。

このため、Statement.cancelメソッドは、ネットワークとデータベースが正しく機能していることに依存します。ネットワーク接続が切断するか、データベース・サーバーがダウンした場合、クライアントは取消しメッセージに対するエラー応答を受け取りません。サーバー・プロセスに障害が発生すると、多くの場合、JDBCはStatement.executeをコールしたスレッドを解放するIOExceptionを受け取ります。ただし、サーバーがダウンしてもJDBCがIOExceptionを受け取らない場合があります。Statement.executeメソッドを開始したスレッドは、Statement.cancelメソッドによって解放されません。


注意:

Statement.cancelメソッドの使用時には次の点に注意してください。
  • 接続レベルの取消しと文レベルの取消しが区別されます。文の実行以外の場合、たとえばROLLBACKstatement.cancelメソッドによって取り消された場合、コマンドがリプレイされます(ROLLBACK, COMMITautoCommit ONautoCommit OFFVERSIONの場合のみ)。データの整合性を保証するために、文の実行をリプレイしません。

  • 取消しのコールがデータベースに送信されるまで実行が戻されないように、文の実行と文の取消しを同期します。これによって、文の実行を取り消せるようになります。

  • 取消しコールを同期します。これにより、処理中の取消しが処理を完了するまで(データベースが中断、実行およびJDBCへの通知を受け取った後)、新しい取消しリクエストが無視されます。


JDBCがIOExceptionを受け取らない場合は、Oracle Netがいずれはタイムアウトし、接続をクローズすることになります。これにより、IOExceptionが発生し、スレッドが解放されます。ただし、このプロセスにはかなりの時間がかかることがあります。このタイムアウトを制御する方法は、OracleDatasource.setConnectionProperties()readTimeoutプロパティの説明を参照してください。このタイムアウトは、Oracle Netの設定を使用してチューニングすることもできます。


関連項目:

詳細は、『Oracle Database Net Services管理者ガイド』を参照してください。

JDBC標準メソッドStatement.setQueryTimeoutは、Statement.cancelメソッドに依存します。指定されたタイムアウト間隔よりも実行時間が長くなった場合、モニター・スレッドがStatement.cancelメソッドをコールします。これは、前述と同じすべての制限を受けます。この結果、Statement.executeメソッドをコールしたスレッドがタイムアウトにより解放されない場合があります。

実行と取消しの間の時間の長さは、それほど正確ではありません。この間隔は、指定されたタイムアウト間隔より短くはなりませんが、数秒間長くなることがあります。アプリケーションに、高い優先順位で実行されているアクティブ・スレッドがある場合、この間隔は随意に長くなることがあります。モニター・スレッドは高い優先順位で実行されますが、他にも高い優先順位のスレッドがあり、それが実行し続けられる場合があります。モニター・スレッドが開始されるのは、ゼロ以外のタイムアウトで実行される文がある場合のみです。すべてのOracle JDBC文の実行を監視するモニター・スレッドは、1つのみあります。


注意:

Statement.cancelメソッドとStatement.setQueryTimeoutメソッドは、サーバー側内部ドライバでサポートされません。サーバー側内部ドライバは単一スレッドのサーバー・プロセスで動作します。Oracle JVMはこの単一スレッド・プロセス内にJavaスレッドを実装します。サーバー側内部ドライバがSQL文を実行している場合、JavaスレッドはStatement.cancelメソッドをコールできません。これは、Oracle JDBC監視スレッドにも適用されます。

ファイアウォールとJDBCの使用方法

アイドル接続に対するファイアウォール・タイムアウトによって、接続が切断される場合があります。このために、JDBCアプリケーションが接続の待機中に停止する可能性があります。ファイアウォール・タイムアウトによって接続が切断されないようにするには、次の処理を1つ以上実行します。

  • 接続キャッシュまたは接続プーリングを使用している場合は、接続キャッシュのInactivityタイムアウト値を、常にファイアウォールのアイドル・タイムアウト値より短い値に設定します。

  • 接続プロパティとしてoracle.jdbc.ReadTimeoutを渡してソケット上で読込みタイムアウトを有効にします。タイムアウト値はミリ秒で指定します。

  • JDBC OCIドライバとJDBC Thinドライバの両方について、ネット記述子を使用してデータベースに接続し、接続記述子のDESCRIPTION句にENABLE=BROKENパラメータを指定します。また、TCP_KEEPALIVE_INTERVALの下限値も設定します。

  • サーバー側のsqlnet.oraファイルにSQLNET.EXPIRE_TIME=1を設定して、Oracle Net DCDを使用可能にします。

多数回にわたるサーバーからの突然の切断

ネットワークの信頼性が低い場合、サーバーの接続が突然切断されても、クライアントでは頻繁に発生する切断を検出するのが困難です。デフォルトでは、Linux上で稼働するクライアントは、突然の切断を検出するのに7200秒(2時間)かかります。この値は、tcp_keepalive_timeプロパティの値と同じです。アプリケーションが切断をより迅速に検出するようにするには、tcp_keepalive_timetcp_keepalive_intervalおよびtcp_keepalive_probesプロパティの値をオペレーティング・システム・レベルでより小さい値に設定する必要があります。


注意:

tcp_keepalive_intervalプロパティに小さい値を設定すると、ネットワーク上にプローブ・パケットが頻繁に送出され、システム速度が低下する可能性があります。そのため、このプロパティの値は、システム要件に基づいて適切に設定してください。

また、接続記述子のDESCRIPTION句にENABLE=BROKENパラメータを指定する必要があります。たとえば、次のようになります。

jdbc:oracle:thin:@(DESCRIPTION=(ENABLE=BROKEN)(ADDRESS=(PROTOCOL=tcp)(PORT=5221)(HOST=myhost))(CONNECT_DATA=(SERVICE_NAME=orcl)))

ネットワーク・アダプタで接続を確立できない

JDBCアプリケーションからOracleインスタンスへの接続を確立しようとしているときに、次のエラーが発生する場合があります。

java.sql.SQLException: Io exception: 
  The Network Adapter could not establish connection
 
SQLException: SQLState (null) vendor code (17002)

このエラーは、次の状況のいずれかまたは全部に当てはまる場合でも発生することがあります。

  • 同じクライアントから同じOracleインスタンスへのSQL*Plus接続を確立できる。

  • JDBC OCI接続は確立できるが、同じクライアントから同じOracleインスタンスへのJDBC Thin接続は確立できない。

  • 同じJDBCアプリケーションが別のクライアントから同じOracleインスタンスに接続できる。

  • 初期JDBC接続文字列がホスト名を指定していようとIPアドレスを指定していようと、同じ動作が適用される。

このエラーの原因として、次の理由の1つ以上が考えられます。

  • 接続を確立しようとしているホスト名が正しくない。

  • 接続の確立に使用しているポート番号が間違っている。

  • NICカードでIPv4とIPv6の両方がサポートされている。

  • OracleインスタンスがMTS用に構成されているのに、JDBC接続で専用サーバーではなく共有サーバーを使用している。

SQL*Plusを使用すると、前述の原因を短時間で診断できます。ただし、NICカードに問題がある場合を除きます。次の各項では、このエラーの解決方法とサンプル・アプリケーションを示します。

MTSサーバーで構成したOracleインスタンスが共有サーバーを使用

このエラーを解決するには、Oracleインスタンスがマルチスレッド・サーバー(MTS)用に構成されているかどうかを確認する必要があります。OracleインスタンスがMTS用に構成されていない場合は、構成する必要があります。

OracleインスタンスがMTS用に構成されている場合は、JDBC接続が共有サーバーではなく専用サーバーを使用するように強制する必要があります。これを行うには、専用接続のみを使用するようにサーバーを再構成します。専用接続のみを使用するようにサーバーを構成することが実現可能ではない場合は、次の手順を実行してクライアント側から設定します。

JDBC OCIクライアントの場合

  1. クライアントのtnsnames.oraファイルに格納されているTNS接続文字列に(SERVER=DEDICATED)プロパティを追加します。

  2. クライアントのsqlnet.oraファイルでUSER_DEDICATED_SERVER=ONを設定します。

JDBC Thinの場合:

短縮JDBC Thin構文ではなく、フルネーム/値ペアの接続文字列(tnsnames.oraファイルに指定されているのと同じ)を指定する必要があります。たとえば、"jdbc:oracle:thin:@host:port:sid"接続文字列ではなく、次の形式の接続文字列を使用する必要があります。

   "jdbc:oracle:thin:@(DESCRIPTION="                   +
                         "(ADDRESS_LIST="              +
                             "(ADDRESS=(PROTOCOL=TCP)" +
                                      "(HOST=host)"    +
                                      "(PORT=port)"    +
                             ")"                       +
                         ")"                           +
                         "(CONNECT_DATA="              +
                             "(SERVICE_NAME=sid)"      +
                             "(SERVER=DEDICATED)"      +
                         ")"                           +
                       ")"

IPv4とIPv6の両方をサポートするNICカードを保有するJDBC Thinドライバ

サーバーのネットワーク・インタフェース・コントローラ(NIC)カードがIPv4とIPv6の両方をサポートするように構成されている場合、サービスによってはIPv6で起動することがあります。IPv6で実行中のサービスにIPv4を使用して(またはその逆)接続しようとするクライアント・アプリケーションはいずれも接続拒否エラーを受け取ります。JDBC thinクライアント・アプリケーションがデータベース・サーバーに接続しようとすると、そのアプリケーションは応答を停止するか、次のエラーで失敗する可能性があります。

java.sql.SQLException: Io exception: The Network Adapter could not establish the connection Error Code: 17002

次の解決策のいずれかを使用してこのエラーを解消してください。

  • Java仮想マシン(JVM)に、IPプロトコル・バージョン4を使用するように指定します。-Djava.net.preferIPv4Stackパラメータをtrueに設定して、JDBCアプリケーションが実行されているJVMを起動します。たとえば、jdbcTestという名前のJDBCアプリケーションを実行しているとします。その場合、アプリケーションを次の方法で実行します。

    java -Djava.net.preferIPv4Stack=true jdbcTest
    
  • OCI JDBCドライバを使用します。

サンプル・アプリケーション

例E-1に、データベースに接続し、接続のテストに使用できる基本的なJDBCプログラムを示します。Oracle JDBCドライバを使用する、あらゆる形式の接続を試すことができます。

例E-1 5つの異なる方法でデータベースに接続する基本的なJDBCプログラム

import java.sql.*;
public class Jdbctest
{
   public static void main (String args[])
   {
         try 
         {
                   /* Uncomment the next line for more connection information */
                   // DriverManager.setLogStream(System.out);          
                   /* Set the host, port, and sid below to match the entries in the listener.ora */         
                   String host = "myhost.oracle.com";
                   String port = "5221";
                   String sid  = "orcl";
                   // or pass on command line arguments for all three items         
                   if ( args.length >= 3 ) 
                   { 
                              host = args[0];
                              port = args[1];            
                              sid  = args[2];
                   }
                   
                   String s1 = "jdbc:oracle:thin:@" + host + ":" + port + ":" + sid ;
                   if ( args.length == 1 )
                   { 
                              s1 =  "jdbc:oracle:oci8:@" + args[0];
                   }
                   if ( args.length == 4 )
                   {
                               s1 = "jdbc:oracle:" + args[3] + ":@" + 
                                    "(description=(address=(host=" + host+ ")(protocol=tcp)(port=" + port+ "))(connect_data=(sid="+ sid + ")))";
                   } 
                   System.out.println( "Connecting with: " );
                   System.out.println( s1 );          
                   DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
                   Connection conn = DriverManager.getConnection( s1,"hr","hr");          
                   DatabaseMetaData dmd = conn.getMetaData();
                   System.out.println("DriverVersion:["+dmd.getDriverVersion()+"]");
                   System.out.println("DriverMajorVersion: ["+dmd.getDriverMajorVersion()+"]");
                   System.out.println("DriverMinorVersion: ["+dmd.getDriverMinorVersion()+"]");
                   System.out.println("DriverName:["+dmd.getDriverName()+"]");
                   if ( conn!=null ) 
                      conn.close();
                   System.out.println("Done.");
           } 
           catch ( SQLException e ) 
           { 
                   System.out.println ("\n*** Java Stack Trace ***\n");
                   e.printStackTrace();          
                   System.out.println ("\n*** SQLException caught ***\n");          
                   while ( e != null ) 
                   {
                               System.out.println ("SQLState: " + e.getSQLState ());             
                               System.out.println ("Message:  " + e.getMessage ());             
                               System.out.println ("Error Code:   " + e.getErrorCode ());             
                               e = e.getNextException ();             
                               System.out.println ("");          
                    }       
             }   
  }
}

基本的なデバッグ処理

JDBCプログラムのデバッグ方法について説明します。

デバッグに役立つスタック・トレースの出力など、SQL例外の処理については、「SQL例外の処理」を参照してください。

ネットワーク・イベントをトラップするためのOracle Netトレース

クライアントおよびサーバーのOracle-Netトレースが、Oracle Net経由で送信されるパケットをトラップできるようにできます。クライアント側トレースはJDBC OCIドライバについてのみ使用できます。JDBC Thinドライバについてはサポートされません。トレースとトレース・ファイルの読取りに関する詳細な情報は、Oracle Net Services管理者ガイドを参照してください。

トレース機能を使用すると、ネットワーク・イベントが実行されるたびにそのイベントについて記述される、一連の詳細な文が生成されます。操作をトレースすることにより、イベントの内部操作に関する詳細な情報を取り出すことができます。この情報は読込み可能ファイルに出力され、エラーの原因となったイベントを特定できます。トレース情報の収集は、SQLNET.ORAファイルにあるいくつかのOracle Netパラメータによって制御されます。SQLNET.ORAのパラメータを設定した後に、トレースを実行するために新しい接続を作成する必要があります。

トレース・レベルを高くすると、より詳細な情報がトレース・ファイルに取得されます。トレース・ファイルの理解が難しくなるため、トレースを有効にする場合はトレース・レベル4から開始します。トレース・ファイルの最初の部分は接続ハンドシェイク情報です。JDBCプログラムに関連するSQL文とエラー・メッセージの情報は、その先を参照してください。


注意:

トレース機能ではディスク領域が大量に使用されるため、システムのパフォーマンスが大幅に低下する可能性があります。トレースは、必要なときにのみ使用可能にしてください。

クライアント側でのトレース

クライアント・システムのSQLNET.ORAファイルに、次のパラメータを設定します。


注意:

Oracle Database 12cリリース1 (12.1)以降、sqlnet.oraファイルにあるOCI固有の構成パラメータのかわりに、新しいXML構成ファイルoraaccess.xmlにある構成パラメータを使用することをお薦めします。ただし、sqlnet.oraファイル内の構成パラメータもまだサポートされています。oraaccess.xmlファイルの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

TRACE_LEVEL_CLIENT

目的

トレースを一定の指定レベルでonまたはoffにします。

デフォルト値

0またはOFF

有効な値

  • 0またはOFF- トレースの出力なし

  • 4またはUSER- ユーザー・トレース情報

  • 10またはADMIN- 管理トレース情報

  • 16またはSUPPORT- カスタマ・サポート・トレース情報

例:

TRACE_LEVEL_CLIENT=10
TRACE_DIRECTORY_CLIENT

目的

トレース・ファイルの書込み先ディレクトリを指定します。

デフォルト値

ORACLE_HOME/network/trace

例:

UNIX: TRACE_DIRECTORY_CLIENT=/oracle/traces

Windows: TRACE_DIRECTORY_CLIENT=C:\ORACLE\TRACES

TRACE_FILE_CLIENT

目的

クライアント・トレース・ファイルの名前を指定します。

デフォルト値

SQLNET.TRC

例:

TRACE_FILE_CLIENT=cli_Connection1.trc


注意:

TRACE_FILE_CLIENTファイルのために選択する名前は、TRACE_FILE_SERVERファイルのために選択する名前と異なっている必要があります。

TRACE_UNIQUE_CLIENT

目的

クライアント側の各トレースに一意の名前を付け、各トレース・ファイルが次に発生したクライアント・トレースによって上書きされないようにします。ファイル名の最後にPIDが付加されます。

デフォルト値

OFF

例:

TRACE_UNIQUE_CLIENT = ON

サーバー側のトレース

サーバー・システムのSQLNET.ORAファイルに次のパラメータを設定します。接続ごとに、一意のファイル名を持つ個別のファイルが生成されます。


注意:

Oracle Database 12cリリース1 (12.1)以降、sqlnet.oraファイルにあるOCI固有の構成パラメータのかわりに、新しいXML構成ファイルoraaccess.xmlにある構成パラメータを使用することをお薦めします。ただし、sqlnet.oraファイル内の構成パラメータもまだサポートされています。oraaccess.xmlファイルの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

TRACE_LEVEL_SERVER

目的

トレースを一定の指定レベルでonまたはoffにします。

デフォルト値

0またはOFF

有効な値

  • 0またはOFF- トレースの出力なし

  • 4またはUSER- ユーザー・トレース情報

  • 10またはADMIN- 管理トレース情報

  • 16またはSUPPORT- カスタマ・サポート・トレース情報

例:

TRACE_LEVEL_SERVER=10
TRACE_DIRECTORY_SERVER

目的

トレース・ファイルの書込み先ディレクトリを指定します。

デフォルト値

ORACLE_HOME/network/trace

例:

TRACE_DIRECTORY_SERVER=/oracle/traces

TRACE_FILE_SERVER

目的

サーバー・トレース・ファイルの名前を指定します。

デフォルト値

SERVER.TRC

例:

TRACE_FILE_SERVER= svr_Connection1.trc


注意:

TRACE_FILE_SERVERファイルのために選択する名前は、TRACE_FILE_CLIENTファイルのために選択する名前と異なっている必要があります。

サード・パーティのデバッグ・ツール

Intersolv社のJDBCSpyおよびJDBCTestなどのツールを使用して、JDBC APIレベルで問題に対処できます。これらのツールはODBC SpyおよびODBC Testツールと類似しています。