web-dev-qa-db-ja.com

ステートメントと結果セットを再利用すると、以前の使用からリソースが解放されますか?または、再利用する前に明示的に閉じる必要がありますか?

サンプルコード:

        aStmt = aConn.prepareStatement(aQuery);
        aRset = aStmt.executeQuery(cQuery);

        while (cRset.next()) {
            //stuff to determine value of parm1

            aStmt.setString(1, parm1);                
            aRset = aStmt.executeQuery(); 

            //more stuff
        }

Whileステートメント内のすべてのループの後にaStmtとaRsetを閉じる必要がありますか?または、後続のループでそれらを再利用すると、前のループから使用されていたメモリ/リソースが解放されますか?

19
heisenbergman

結果セットと(プリペアド)ステートメントの動作は、Java APIに明示的に文書化されています。詳細については、実際の文書(およびJDBC仕様)を読むことをお勧めします。

Statement APIによると:

デフォルトでは、ResultSetオブジェクトごとに1つのStatementオブジェクトのみを同時に開くことができます。したがって、あるResultSetオブジェクトの読み取りが別のオブジェクトの読み取りとインターリーブされている場合、それぞれが異なるStatementオブジェクトによって生成されている必要があります。 Statementインターフェイスのすべての実行メソッドは、開いているオブジェクトが存在する場合、ステートメントの現在のResultSetオブジェクトを暗黙的に閉じます。

(私の強調)。

特定のコードでは、aStmt.executeQuery()を呼び出すと、ResultSetに割り当てられた古いaRsetがドライバーによって暗黙的に閉じられます。とはいえ、最後にResultSetを閉じるのを忘れないように、自分で明示的に閉じる(またはJava 7 try-with-resources)を使用する)方がよいでしょう。ループの反復。

PreparedStatementへ:ステートメントを準備すると(一般に、実装は異なる場合があります)、クエリはコンパイルのためにサーバーに送信されます。実行時に、その特定の実行のパラメーターがサーバーに送信されます。 aStmtclose()を呼び出すと、準備されたステートメントがサーバー上で割り当て解除されます。つまり、明らかに[〜#〜] not [〜#〜] パラメーターに異なる値を使用してステートメントを再利用したいので、ここで必要なもの。

つまり、

  1. ここではResultSetを閉じる必要はありませんが(最後に作成されたResultSetを除く)、明示的に閉じることをお勧めします。
  2. PreparedStatementは、使い終わったときにのみ閉じる必要があります。

try-with-resources を使用することは、これらの問題に関する混乱の一部を取り除く1つの方法です。コードが完了すると(使用範囲の最後に)リソースが自動的に解放されるためです。

try (
    ResultSet cRset = cStmt.executeQuery(cQuery);
    PreparedStatement aStmt = aConn.prepareStatement(aQuery);
) {
    while (cRset.next()) {
        //stuff to determine value of parm1

        aStmt.setString(1, parm1);                
        try (ResultSet aRset = aStmt.executeQuery()) {
            //more stuff
        }
    }
}

このコードの最後で、すべてのJDBCリソースが正しく閉じられます(例外が発生した場合などでも、正しい順序で)

25
Mark Rotteveel

いいえ、ResultSetループ内でStatementwhileを閉じることはできません。

ループの後でそれらを閉じる必要があります。

また、PreparedStatementを再利用する場合は、処理の準備が整うまで閉じないでください。

最善のルールは、そのようなリソースが作成されたのと同じブロックで閉じることです。あなたの場合、最善の方法は、finallyをキャッチした後、SQLExceptionブロック内のリソースを閉じることです。

例えば。

try {
    aStmt = aConn.prepareStatement(aQuery);
    cRset = cStmt.executeQuery(cQuery);

    while (cRset.next()) {
        //stuff to determine value of parm1

        aStmt.setString(1, parm1);
        try {
            aRset = aStmt.executeQuery();
        } finally {
            aRset.close();
        }

        //more stuff
    }
} catch (SQLException ex) {
    // Do error handling
} finally {
    // Close Resultset
}

Java 7では、リソースでtryを使用できます。

2
Uwe Plonus

PreparedStatement API:SQLステートメントはプリコンパイルされ、PreparedStatementオブジェクトに格納されます。次に、このオブジェクトを使用して、このステートメントを複数回効率的に実行できます。

ただし、ResultSetオブジェクトを再利用することはできません。 2回目に新しいResultSetが作成されたときにPreparedStatementオブジェクトでexecuteQueryを呼び出すときに、前のResultSetを閉じないと、リソースリークが発生するリスクがあります。

0