- PR -

ResultSet/Statementのクローズについて。

投稿者投稿内容
たかし
常連さん
会議室デビュー日: 2004/10/15
投稿数: 27
投稿日時: 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をセットしても、システム上、問題ないのでしょうか??

すみませんが、アドバイスのほど、よろしくお願いいたします。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2005-02-04 01:03
nullをセットしてもインスタンスの破棄はJavaVMに委ねられます。
該当オブジェクトに対し参照がないと判断されてからJavaVMによって破棄されます。
nullのセットはJavaVMに対して、
「ここのスコープでは参照していません。」
ということを通知しているに過ぎません。

ちなみに「close」と「nullをセット」は意味が違います。
大抵のJDBCドライバはfinalizeメソッドでcloseを自動で行っていますが、
GCが行われなければいつまでもcloseは行われません。

何かしらの原因にてResultSetが閉じた状態になっていると思います。
ですので、既に閉じられているResultSetに対して
closeを実行している為例外が発生しています。
Connectionの使いまわしとかしていませんかね。
K
大ベテラン
会議室デビュー日: 2004/04/07
投稿数: 174
投稿日時: 2005-02-04 01:09
直接的な回答はかつのりさんがしてくれていますが、JDBCのコーディングは冗長になりがちなので(バグの遠因になりやすいので)、可能であればJakarta DbUtilsなどを使用することをお勧めします。
O/R mappingほど大掛かりではないので簡単に導入できます。
kito
ベテラン
会議室デビュー日: 2003/03/24
投稿数: 59
お住まい・勤務地: Osaka
投稿日時: 2005-02-04 01:19
引用:

Sampleさんの書き込み (2005-02-04 00:37) より:

nullをセットしてインスタンスを直ぐに破棄出来るものなのでしょうか?


変数へのnull代入は通常は全く無意味、あるいは有害です。


引用:

あと、close()メソッドの代わりにnullをセットしても、システム上、問題ないのでしょうか??


非常に問題です。
close()で開放しなければならない貴重なリソースを開放しないままに放って置くことになりますから。
こういうのをリソースリークと呼びます。

引用:

if (rs != null) {


このrsをどこで宣言して、どう使っていますか?
おそらく変数rs及びResultSetオブジェクトの誤った共有を行っていると考えられます。
close()されないままのResultSetオブジェクトの残骸がたくさん残っていることでしょう。
未記入
ぬし
会議室デビュー日: 2004/09/17
投稿数: 667
投稿日時: 2005-02-04 01:29
データベースやファイルなんかの後処理(closeなど)が例外を投げる可能性があると、finally節に書くにしても面倒ですね。moge さんの紹介している DBUtils なんかだと、例外を握りつぶしてしまう closeQuietly() などが用意されています。( Java の作法として良いかどうかはともかく。)

ResultSet の close() で例外が発生するのは、ほとんど「すでに閉じられている」のが原因でしょうから、closeQuietly() のように例外を握りつぶしてしまうのもアリだと思います。
YOU@IT
ぬし
会議室デビュー日: 2002/03/29
投稿数: 284
お住まい・勤務地: 大阪
投稿日時: 2005-02-04 09:17
根本原因は、変数の再利用にあるのではないでしょうか?

StatementやResultの変数に何度も参照を代入するようなコーディングをするよりも、
毎回変数を宣言したほうが安全だと思います。

StatementやResultSetの変数を再利用して同様のエラーが発生しているのを何度も見ましたから。。。
たかし
常連さん
会議室デビュー日: 2004/10/15
投稿数: 27
投稿日時: 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の変数に何度も参照を代入するようなコーディングをするよりも、
>毎回変数を宣言したほうが安全だと思います。
ローカル変数で宣言するべきでしょうか。。。
永井和彦
ぬし
会議室デビュー日: 2002/07/03
投稿数: 276
お住まい・勤務地: 東京都
投稿日時: 2005-02-04 19:14
引用:

セションにコネクションを保持しており、あるイベントが発生すると必ず、



HttpSesion内にコネクションを保持するのはお勧めしません。
コネクションプールから必要なときに取得して、やることだけやってすぐにプールに返却する方法が一般的だと思います。

引用:

>StatementやResultの変数に何度も参照を代入するようなコーディングをするよりも、
>毎回変数を宣言したほうが安全だと思います。
ローカル変数で宣言するべきでしょうか。。。



Connection、Statement、ResultSetはローカル変数として宣言してfinally句でcloseする方法が一般的だと思います。

スキルアップ/キャリアアップ(JOB@IT)