web-dev-qa-db-ja.com

接続が閉じられたときにResultSetが閉じられていませんか?

私のペットプロジェクトの1つでコードレビュー(主にFindBugsなどのツールを使用)を行っており、FindBugsは次のコードをエラー(疑似コード)としてマークしました。

Connection conn = dataSource.getConnection();

try{
    PreparedStatement stmt = conn.prepareStatement();
    //initialize the statement
    stmt.execute();
    ResultSet rs =  stmt.getResultSet();
    //get data
}finally{
    conn.close();
}

エラーは、このコードがリソースを解放しない可能性があることでした。 ResultSetとStatementが閉じられていなかったため、最終的に閉じました。

finally{
    try{
        rs.close()
    }catch(SqlException se){
        //log it
    }
    try{
        stmt.close();
    }catch(SqlException se){
        //log it
    }
    conn.close();
}

しかし、多くのプロジェクト(かなりの数の企業)で上記のパターンに遭遇し、ResultSetsまたはStatementsを閉じる人はいませんでした。

Connectionが閉じられたときにResultSetとStatementが閉じられないという問題がありましたか?

this のみが見つかり、Connectionsを閉じるときにResultSetを閉じる際に問題が発生するOracleを参照しています(Oracle dbを使用しているため、修正します)。 Java.sql.apiはConnection.close()javadocで何も言いません。

49
jb.

結果セットではなく、接続のみを閉じることに関する1つの問題は、接続管理コードが接続プーリングを使用している場合、connection.close()は単に接続をプールに戻すことです。さらに、一部のデータベースには、明示的に閉じられない限り適切に解放されないカーソルリソースがサーバー上にあります。

52
Aaron

接続が閉じられたにもかかわらず、Oracleで閉じられていないResultSetで問題が発生しました。私が得たエラーは

"ORA-01000: maximum open cursors exceeded"

だから:常にResultSetを閉じてください!

28
neu242

すべてのJDBCリソースを常に明示的に閉じる必要があります。アーロンとジョンがすでに言ったように、接続を閉じるとプールに返されることが多く、すべてのJDBCドライバーがまったく同じ方法で実装されるわけではありません。

Finallyブロックから使用できるユーティリティメソッドを次に示します。

public static void closeEverything(ResultSet rs, Statement stmt,
        Connection con) {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) {
        }
    }
    if (stmt != null) {
        try {
            stmt.close();
        } catch (SQLException e) {
        }
    }
    if (con != null) {
        try {
            con.close();
        } catch (SQLException e) {
        }
    }
}
19

この場合、Oracleはオープンカーソルに関するエラーを表示します。

準拠: http://Java.Sun.com/javase/6/docs/api/Java/sql/Statement.html

ステートメントを再利用すると開いている結果セットが閉じられ、ステートメントを閉じると結果セットが閉じられるように見えますが、接続を閉じると作成されたリソースが閉じられることはありません。

これらの詳細はすべて、JDBCドライバープロバイダーに任されています。

すべてを明示的に閉じるのが常に最も安全です。 try {xxx} catch(Throwable {}ですべてをラップするutilクラスを作成したため、Utils.close(rs)やUtils.close(stmt)などを呼び出すだけで、スキャンを閉じる例外を心配する必要はありません。 。

9
John Gardner

ODBC Bridgeは、いくつかのODBC=ドライバでメモリリークを引き起こす可能性があります。

適切なJDBCドライバーを使用する場合、接続を閉じる際に問題は発生しないはずです。しかし、2つの問題があります。

  • あなたが良いドライバーを持っているかどうか知っていますか?
  • 将来、他のJDBCドライバーを使用しますか?

ベストプラクティスは、すべてを閉じることです。

8
Horcrux7

私は大規模なJ2EE Web環境で働いています。 1つのリクエストで接続できるデータベースがいくつかあります。一部のアプリケーションで論理的なデッドロックが発生し始めました。問題は次のとおりでした。

  1. ユーザーがページをリクエストします
  2. サーバーはDB 1に接続します
  3. サーバーはDB 1で選択します
  4. サーバーはDB 1への接続を「閉じます」
  5. サーバーはDB 2に接続します
  6. デッドロック!

これは2つの理由で発生しました。通常よりもはるかに大量のトラフィックが発生し、デフォルトではJ2EE仕様はスレッドの実行が完了するまで実際に接続を閉じません。したがって、上記の例では、ステップ4が最終的に適切に閉じられたにもかかわらず、実際に接続を閉じたことはありません。

これを修正するには、データベース接続のweb.xmlでリソース参照を使用し、res-sharing-scopeを共有不可に設定する必要があります。

例:

<resource-ref>
    <description>My Database</description>
    <res-ref-name>jdbc/jndi/pathtodatasource</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Unshareable</res-sharing-scope>
</resource-ref>
8
Konrad

閉じられていないResultSetに問題があることは間違いありませんが、常に閉じると何が問題になりますか?これを覚えておく必要があるという信頼性の低さは、これらの詳細を管理するフレームワークに移行する最良の理由の1つです。開発環境では実現不可能かもしれませんが、Springを使用してJPAトランザクションを管理することは非常に幸運でした。開いている接続、ステートメント、結果セット、および過度に複雑なtry/catch/finallyブロック(try/catchブロックをfinallyブロックに書き込む)の厄介な詳細!)それらを再び閉じると、消えてしまい、実際に作業を完了することができます。この種のソリューションに移行することを強くお勧めします。

4
JavadocMD

Javaでは、(結果セットではなく)ステートメントはOracleのカーソルに相関します。 JVMおよびシステムリソースに関して予期しない動作が発生する可能性があるため、開いているリソースを閉じることをお勧めします。

さらに、一部のJDBCプーリングフレームワークはステートメントと接続をプールするため、それらを閉じないと、それらのオブジェクトがプール内で空きとしてマークされず、フレームワークでパフォーマンスの問題が発生する場合があります。

一般に、オブジェクトにclose()またはdestroy()メソッドがある場合、それを呼び出す理由があり、それを無視するのはあなた自身の責任で行われます。

4
Spencer Kormos