- - PR -
ResultSet/Statementのクローズについて。
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2005-02-04 00:37
いつもお世話になっております。
サーブレット(struts)でDBへアクセスしデータを検索、更新などを行っています。 通常、finally句でResultSetやStatementをクローズすると思いますが、 close()メソッドを実行すると、時々、SQLExceptionが発生します。 以下、SQLExceptionの一部です。 --------------- java.sql.SQLException: ResultSet is closed at sun.jdbc.odbc.JdbcOdbcResultSet.checkOpen(JdbcOdbcResultSet.java:6650) at sun.jdbc.odbc.JdbcOdbcResultSet.clearWarnings(JdbcOdbcResultSet.java:1767) at sun.jdbc.odbc.JdbcOdbcResultSet.close(JdbcOdbcResultSet.java:1470) --------------- そこで、周囲に相談したのですが、インスタンスが消えていないから、nullをセットしてみては、と言われ、その通りにやったのですが、SQLExceptionは発生しなくなりました。 以下、記述例です。 --------------- if (rs != null) { rs.close(); rs = null; } または、 if (rs != null) { rs = null; } --------------- そこで疑問があって、インスタンスの破棄はVMに委ねられていると思っていましたが、nullをセットしてインスタンスを直ぐに破棄出来るものなのでしょうか? あと、close()メソッドの代わりにnullをセットしても、システム上、問題ないのでしょうか?? すみませんが、アドバイスのほど、よろしくお願いいたします。 | ||||||||||||
|
投稿日時: 2005-02-04 01:03
nullをセットしてもインスタンスの破棄はJavaVMに委ねられます。
該当オブジェクトに対し参照がないと判断されてからJavaVMによって破棄されます。 nullのセットはJavaVMに対して、 「ここのスコープでは参照していません。」 ということを通知しているに過ぎません。 ちなみに「close」と「nullをセット」は意味が違います。 大抵のJDBCドライバはfinalizeメソッドでcloseを自動で行っていますが、 GCが行われなければいつまでもcloseは行われません。 何かしらの原因にてResultSetが閉じた状態になっていると思います。 ですので、既に閉じられているResultSetに対して closeを実行している為例外が発生しています。 Connectionの使いまわしとかしていませんかね。 | ||||||||||||
|
投稿日時: 2005-02-04 01:09
直接的な回答はかつのりさんがしてくれていますが、JDBCのコーディングは冗長になりがちなので(バグの遠因になりやすいので)、可能であればJakarta DbUtilsなどを使用することをお勧めします。
O/R mappingほど大掛かりではないので簡単に導入できます。 | ||||||||||||
|
投稿日時: 2005-02-04 01:19
変数へのnull代入は通常は全く無意味、あるいは有害です。
非常に問題です。 close()で開放しなければならない貴重なリソースを開放しないままに放って置くことになりますから。 こういうのをリソースリークと呼びます。
このrsをどこで宣言して、どう使っていますか? おそらく変数rs及びResultSetオブジェクトの誤った共有を行っていると考えられます。 close()されないままのResultSetオブジェクトの残骸がたくさん残っていることでしょう。 | ||||||||||||
|
投稿日時: 2005-02-04 01:29
データベースやファイルなんかの後処理(closeなど)が例外を投げる可能性があると、finally節に書くにしても面倒ですね。moge さんの紹介している DBUtils なんかだと、例外を握りつぶしてしまう closeQuietly() などが用意されています。( Java の作法として良いかどうかはともかく。)
ResultSet の close() で例外が発生するのは、ほとんど「すでに閉じられている」のが原因でしょうから、closeQuietly() のように例外を握りつぶしてしまうのもアリだと思います。 | ||||||||||||
|
投稿日時: 2005-02-04 09:17
根本原因は、変数の再利用にあるのではないでしょうか?
StatementやResultの変数に何度も参照を代入するようなコーディングをするよりも、 毎回変数を宣言したほうが安全だと思います。 StatementやResultSetの変数を再利用して同様のエラーが発生しているのを何度も見ましたから。。。 | ||||||||||||
|
投稿日時: 2005-02-04 18:48
返信、ありがとうございます。
動きについて、もう少し説明します。 セションにコネクションを保持しており、あるイベントが発生すると必ず、setAutoCommit(false)とconn.commit()、ResultSet#close()、Statement#close()を実行します。 なので、DBへアクセスの処理がない場合でも上の処理は行われます。 上の条件の下、イベントAでDBへアクセスし、次に、イベントBが発生した場合はDBへアクセスしないという動きになっています。 かつのりさんがご指摘しているように、ResultSetがイベントAで閉じられており(closeの実行)、次のイベントBの時にResultSetのオブジェクトが残っている状態でcloseを実行しようとしてSQLExceptionが発生しているように見えます。 >Connectionの使いまわしとかしていませんかね。 セションにConnectionのオブジェクトを保持している状態なので、使いまわしています。 すみません、使いまわすことの危険性が分からないのですが・・・ >このrsをどこで宣言して、どう使っていますか? >おそらく変数rs及びResultSetオブジェクトの誤った共有を行っていると考えられます。 >close()されないままのResultSetオブジェクトの残骸がたくさん残っていることでしょう。 グローバル変数で宣言し、nullをセットしています。 >StatementやResultの変数に何度も参照を代入するようなコーディングをするよりも、 >毎回変数を宣言したほうが安全だと思います。 ローカル変数で宣言するべきでしょうか。。。 | ||||||||||||
|
投稿日時: 2005-02-04 19:14
HttpSesion内にコネクションを保持するのはお勧めしません。 コネクションプールから必要なときに取得して、やることだけやってすぐにプールに返却する方法が一般的だと思います。
Connection、Statement、ResultSetはローカル変数として宣言してfinally句でcloseする方法が一般的だと思います。 |