Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

関数で返却されたResultSetを使いたい場合のcloseタイミングについて

関数で取得したResultSetを、戻り値として使いたい場合のcloseのタイミングについてご教授ください。
具体的には以下のような処理を考えています。
気にしているのは、参照渡しといえどアドレスの値渡しをしているだけなので、呼び出し先でクローズしなくても、以下のソースで正しくクローズできているのかが知りたいです。

--
【呼び出し側】
ResultSet rs = null;
PreaparedStatment ps = null;

try {

    // ResultSet取得
    rs = selectQuery(rs, ps, param);
    } catch (Exception e) {
        エラー処理
    } finally {
        try {
            if (rs != null) {
                rs.close();
            }
            if (ps != null) {
                ps.close();
            }
        } catch (SQLException e) {
            // 何もしない
        }
    }
    
--
【呼び出され側】
public ResultSet selectQuery(ResultSet rs, PreparedStatment ps, String param) {
    // 諸々省略 
    try {
        ps = dba.createPreparedStatment(SQL文)
    
        // パラメータ設定
        ps.setString(1, param);
    
        // SQL実行
        rs = dba.select(ps);
    } catch (SQLException e) {
        エラー処理
    }
    return rs;
}

7

回答

92419

閲覧

7件の回答

評価

30

「書いてある順番に、書いてある通りに動く」という基本を考えれば良い。
AさんがBさんにデータを渡しがてら「Bさんの作業が終ったら、Cさんに『これメールして』と言って」と伝えるのを考えてみようか。
Cさんは同じ人なんだから、(Bさんが言い忘れない限り)Cさんはちゃんとメールを送る。

私がソースレビューするとしたら、確認する点は、
・呼び出し側で引数に渡しているResultSet、PreparedStatementに値が入ると考えているのか
・呼び出し側のcatchはunreachableなのではないか
・rs.close()で例外が発生した場合に、ps.close()がどうなるか・どうあるべきかを考えているか
辺りだろうな。

それから、細かいがselectQuery()は「関数」とは言わない。

評価

0

お礼が遅くなってしまい申し訳ありません。
ご回答ありがとうございます。


問題なしと理解しました。
またレビューまでしていただきありがとうございます。

>・呼び出し側で引数に渡しているResultSet、PreparedStatementに値が入ると考えているのか
渡しているのはアドレス値なので、そのアドレスが指すメモリに値が入る、という認識です。

>・呼び出し側のcatchはunreachableなのではないか
省略してしまいましたが、呼び出され側のエラー処理で、SQLExceptionをスローするイメージです。

>・rs.close()で例外が発生した場合に、ps.close()がどうなるか・どうあるべきかを考えているか
そこでもSQLExceptionをcatchすべきですね。

>それから、細かいがselectQuery()は「関数」とは言わない。
C言語をやってる時期が長かったので思わす使ってしまいました。
メソッドですね。

評価

0

>渡しているのはアドレス値なので、そのアドレスが指すメモリに値が入る、という認識です。
ああ、なるほど…。

selectQuery(ResultSet rs~)は、Cで言う(struct ResultSet *rs~)ではない。*無しだ。
つまりps = dba.createPreparedStatment(SQL文)は…長くやっていたというなら伝わるだろう。

評価

0

>つまりps = dba.createPreparedStatment(SQL文)は…長くやっていたというなら伝わるだろう
新しいPreparedStatementオブジェクト生成してるので、参照先が変わってしまっていて(rsも同様)
この時点で、引数で渡しているものとは別物になってしまっている、ということでしょうか。
呼び出し側での「ps」はnullのまま(デバッグで確認しました)で、selectQuery内で生成されたPreparedStatement、ResultSetオブジェクトはcloseされていないと。。。

上記認識で合ってる場合、呼び出し側でselectQuery内で生成されたps、rsを開放することはできるのでしょうか?
訳あってrsを戻り値としたいので、selectQuery内でcloseは出来ないのです。。。

評価

0

>引数で渡しているものとは別物になってしまっている、ということでしょうか。
そういうことだね。
実際にはどこかのタイミングで自動的に破棄されるが、「要らなくなったら明示的に破棄」に異論ないはず。

メソッドから複数のオブジェクトをシンプルに戻す手段は、Javaにはない(フィールド…Cでいうグローバル変数はひとまず考えない)。
それなら、Cでstructにstructを入れるように「オブジェクトをまとめたオブジェクト」を使えば良い。
void selectQuery(Object[] sets~)
が一番単純か。sets自体はselectQueryで書き換えないのだから、設定された配列の中身は呼び出し元でもそのまま使える。

ただ、これは元のソースからなんだが、ここではそもそも「引数にnullを渡す」こと自体に意味がないので、
Object[] selectQuery(~)
と引数から追い出した方が良い。
さらに頑張るなら、Object[]ではなく専用のクラスを書いても良い。

評価

0

ついでに書いておくと…。

全体的な設計に拠るが、生成と解放は近い方が好ましい。PreparedStatementもResultSetも、selectQuery()がclose()まで責任を持つ方が良い。
以下のように考えられる。

1.POJOに置き換える。
selectQuery()内でResultSetをループし、全部取り出してList<Map<String,Object>>を返すようにする。ただし選択件数が多い場合はメモリ消費が大きいかも知れない。

2.コールバックを用いる。
引数にConsumer<ResultSet>を渡す。selectQuery()内でResultSetをループし、1件fetchするごとにResultSetを引数にコールバックを呼び出す。

評価

0

1はもちろんList<DTO>も考えられるが、汎化するにはリフレクションが必要になるので、全体から見たselectQuery()の位置づけにも拠る。
エラー処理まで考えると手間だし。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。