49

私はペット プロジェクトの 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();
}

しかし、(かなりの数の企業の) 多くのプロジェクトで上記のパターンに遭遇しましたが、誰も ResultSet やステートメントを閉じていませんでした。

接続が閉じられたときに ResultSet とステートメントが閉じられないという問題がありましたか?

私はこれだけを見つけました.Oracleが接続を閉じるときにResultSetを閉じるのに問題があることを示しています(Oracle dbを使用しているため、私の修正です)。java.sql.api は Connection.close() javadoc で何も言いません。

4

8 に答える 8

52

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

于 2008-09-19T18:00:44.100 に答える
29

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

"ORA-01000: maximum open cursors exceeded"

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

于 2008-09-19T17:42:14.940 に答える
19

すべてのJDBCリソースを常に明示的に閉じる必要があります。AaronとJohnがすでに述べたように、接続を閉じるとプールに戻るだけで、すべての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) {
        }
    }
}
于 2008-09-19T19:32:00.980 に答える
9

この場合、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) などを呼び出すことができ、クローズ スキャンがスローすると思われる例外について心配する必要はありません。 .

于 2008-09-19T17:50:15.297 に答える
8

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

  1. ユーザーはページをリクエストします
  2. サーバーはDB1に接続します
  3. DB1でサーバーが選択
  4. サーバーはDB1への接続を「閉じます」
  5. サーバーはDB2に接続します
  6. 行き詰まった!

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

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

例:

<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>
于 2008-09-19T20:07:18.947 に答える
8

ODBC ブリッジは、一部の ODBC ドライバーでメモリ リークを引き起こす可能性があります。

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

  • あなたは良いドライバーを持っているかどうか知っていますか?
  • 今後、他の JDBC ドライバーを使用する予定はありますか?

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

于 2008-09-19T18:53:01.137 に答える
4

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

于 2008-09-19T17:56:19.587 に答える
4

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

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

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

于 2008-09-19T18:00:03.650 に答える