接続が閉じられたときに 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();
}
しかし、私は多くのプロジェクト (かなりの数の企業の) で上記のパターンに遭遇しましたが、誰も ResultSet や Statements を閉じていませんでした。
接続が閉じられたときに ResultSet と Statements が閉じられないという問題が発生しましたか?
だけ見つけました これ これは、Oracle が接続を閉じるときに ResultSet を閉じる際に問題があることを示しています (Oracle データベースを使用しているため、修正しました)。java.sql.api は Connection.close() javadoc で何も述べません。
解決
結果セットではなく接続のみを閉じる場合の問題の 1 つは、接続管理コードが接続プーリングを使用している場合、 connection.close()
接続をプールに戻すだけです。さらに、一部のデータベースではサーバー上にカーソル リソースがあり、明示的に閉じない限り適切に解放されません。
他のヒント
接続が閉じられているにもかかわらず、Oracle で閉じられていない ResultSet に関する問題が発生しました。私が得たエラーは
"ORA-01000: maximum open cursors exceeded"
それで:ResultSet は常に閉じてください。
常にすべての 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) {
}
}
}
この場合、Oracle はオープン カーソルに関するエラーを表示します。
によると: http://java.sun.com/javase/6/docs/api/java/sql/Statement.html
ステートメントを再利用すると開いている結果セットがすべて閉じられ、ステートメントを閉じると結果セットがすべて閉じられるようですが、接続を閉じると作成されたリソースがすべて閉じることについては何も表示されません。
これらの詳細はすべて JDBC ドライバー プロバイダーに委ねられます。
すべてを明示的に閉じることが常に最も安全です。try{ xxx } catch (Throwable {}) ですべてをラップする util クラスを作成しました。これにより、close scan がスローすると思われる例外を気にせずに、Utils.close(rs) や Utils.close(stmt) などを呼び出すことができます。 。
ODBC ブリッジは、一部の ODBC ドライバーでメモリ リークを引き起こす可能性があります。
優れた JDBC ドライバーを使用している場合は、接続を閉じる際に問題は発生しません。しかし、問題が 2 つあります。
- 優秀なドライバーがいるかどうか知っていますか?
- 将来的に他の JDBC ドライバーを使用する予定ですか?
ベストプラクティスはすべてを閉じることです。
私は大規模な J2EE Web 環境で作業しています。1 回のリクエストで接続できるデータベースが複数あります。一部のアプリケーションで論理デッドロックが発生し始めました。問題は次のようなものでした。
- ユーザーはページをリクエストします
- サーバーは DB 1 に接続します
- サーバーは DB 1 を選択します
- サーバーが DB 1 への接続を「閉じる」
- サーバーは DB 2 に接続します
- 行き詰まった!
これは 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>
閉じられていない ResultSet に関する問題を確かに見たことがありますが、それらを常に閉じていると何が問題になるでしょうか?これを忘れずに行う必要があるという信頼性のなさが、これらの詳細を管理するフレームワークに移行する最大の理由の 1 つです。開発環境では実現できないかもしれませんが、Spring を使用して JPA トランザクションを管理するのは非常に幸運でした。接続、ステートメント、結果セットのオープン、および過度に複雑な try/catch/finally ブロックの作成に関する厄介な詳細 (try/catch ブロックを使用) ファイナルブロックで!) 再度閉じると表示されなくなるだけなので、実際に作業を行う必要があります。そのようなソリューションに移行することを強くお勧めします。
Java では、ステートメント (結果セットではない) が Oracle のカーソルに関連付けられます。JVM およびシステム リソースに関して予期しない動作が発生する可能性があるため、開いたリソースは閉じることをお勧めします。
さらに、一部の JDBC プーリング フレームワークはステートメントと接続をプールするため、それらを閉じないと、それらのオブジェクトがプール内で空きとしてマークされず、フレームワークでパフォーマンスの問題が発生する可能性があります。
一般に、オブジェクトに close() または destroy() メソッドがある場合、それを呼び出す理由があり、無視する場合は自己責任で行ってください。