私のペットプロジェクトの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で何も言いません。
結果セットではなく、接続のみを閉じることに関する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クラスを作成したため、Utils.close(rs)やUtils.close(stmt)などを呼び出すだけで、スキャンを閉じる例外を心配する必要はありません。 。
ODBC Bridgeは、いくつかのODBC=ドライバでメモリリークを引き起こす可能性があります。
適切なJDBCドライバーを使用する場合、接続を閉じる際に問題は発生しないはずです。しかし、2つの問題があります。
ベストプラクティスは、すべてを閉じることです。
私は大規模なJ2EE Web環境で働いています。 1つのリクエストで接続できるデータベースがいくつかあります。一部のアプリケーションで論理的なデッドロックが発生し始めました。問題は次のとおりでした。
これは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>
閉じられていないResultSetに問題があることは間違いありませんが、常に閉じると何が問題になりますか?これを覚えておく必要があるという信頼性の低さは、これらの詳細を管理するフレームワークに移行する最良の理由の1つです。開発環境では実現不可能かもしれませんが、Springを使用してJPAトランザクションを管理することは非常に幸運でした。開いている接続、ステートメント、結果セット、および過度に複雑なtry/catch/finallyブロック(try/catchブロックをfinallyブロックに書き込む)の厄介な詳細!)それらを再び閉じると、消えてしまい、実際に作業を完了することができます。この種のソリューションに移行することを強くお勧めします。
Javaでは、(結果セットではなく)ステートメントはOracleのカーソルに相関します。 JVMおよびシステムリソースに関して予期しない動作が発生する可能性があるため、開いているリソースを閉じることをお勧めします。
さらに、一部のJDBCプーリングフレームワークはステートメントと接続をプールするため、それらを閉じないと、それらのオブジェクトがプール内で空きとしてマークされず、フレームワークでパフォーマンスの問題が発生する場合があります。
一般に、オブジェクトにclose()またはdestroy()メソッドがある場合、それを呼び出す理由があり、それを無視するのはあなた自身の責任で行われます。