接続は後で閉じられますが、JDBC結果セットとステートメントは別々に閉じる必要がありますか?


256

使用後はすべてのJDBCリソースを閉じるのがよい習慣だと言われています。しかし、次のコードがある場合、結果セットとステートメントを閉じる必要がありますか?

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    try { if (rs != null) rs.close(); } catch (Exception e) {};
    try { if (stmt != null) stmt.close(); } catch (Exception e) {};
    try { if (conn != null) conn.close(); } catch (Exception e) {};
}

問題は、接続を閉じることで問題が解決するか、それとも一部のリソースが使用中のままになるかです。


回答:


199

あなたがしたことは完璧でとても良い習慣です。

私がその良い習慣を言う理由...たとえば、何らかの理由で「プリミティブ」タイプのデータベースプーリングを使用しているときにを呼び出したconnection.close()場合、接続はプールに返され、ResultSet/ Statementは決して閉じられないため、多くの異なる新しい問題が発生します!

したがってconnection.close()、クリーンアップに常に期待することはできません。

これが役に立てば幸いです:)


4
...そして明示的にすべてを閉じる最も明白な理由。
Zeemee

2
結果セットとステートメントを閉じることは良い習慣であることに同意します。ただし、結果セットとステートメントはガベージコレクションされます-それらは永久に開いたままになるわけではなく、「さまざまな新しい問題に遭遇する」ことはありません。
ステパニアン

3
@ラルフ・スティーブンス-あなたはそれを当てにすることはできません。ガベージコレクションされた後でも、ResultSetが閉じられなかったため、MSSQL JDBCドライバーがメモリリークを起こす状況がありました。
ポール

7
@Paul-興味深い。これは、JDBCドライバーの欠点のように思えます。
ステパニアン

2
@tleb-期待どおりに動作します。理論的には、例外は「高価」であるため、非常に小さなパフォーマンスノックが発生します(すでに確認済みです)
Paul

124

Java 1.7では、try-with-resourcesステートメントのおかげで私たちの生活がはるかに簡単になりました。

try (Connection connection = dataSource.getConnection();
    Statement statement = connection.createStatement()) {
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do stuff with the result set.
    }
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do more stuff with the second result set.
    }
}

この構文は非常に簡潔でエレガントです。そして、connection実際にしても閉じられますstatement作成することができませんでした。


56
このようにネストする必要はありません。1つのtry-with-resourcesですべて実行できます。リソース宣言を個別のステートメント(で区切られた;)として扱います
Mark Rotteveel

2
Mark Rotteveel:3つのConnection、Statement、およびResultSetのすべてに対して1回の試行を使用できますが、複数のクエリを実行する場合は、新しいクエリを開始する前に前のResultSetを閉じる必要があります。少なくとも、これが私が使用していたDBMSの動作方法です。
ラウル・サリナス- Monteagudo

なんでこんなことしないの?try(open connection){try(multiple statements&resultsets){特に、次のクエリの結果が前のクエリで計算できる場合。
Daniel Hajduk 2015年

ダニエル:そのパターンを使用したとき、基礎となるJDBCバックエンドはResultSetを開いたままにして2番目のものを開くことをサポートしていませんでした。
ラウル・サリナス- Monteagudo

rascio、あなたはcatchブロックに必要なものは何でも行うことができます
ラウル・サリナス-Monteagudo

73

以下からのJavadoc

ときにStatementオブジェクトが閉じられ、その電流ResultSetが存在する場合、オブジェクトは、また閉じられます。

ただし、Javadocは、基礎となるを閉じるときにStatementResultSetが閉じられるかどうかについてはあまり明確ではありませんConnection。彼らは単に接続を閉じると述べています:

このConnectionオブジェクトのデータベースとJDBCリソースが自動的に解放されるのを待つのではなく、すぐに解放します。

私の意見では、常に明示的に近いResultSetsStatementsConnectionsあなたはの実装として、それらを終了しているとき、closeデータベースドライバの間で変化させることができます。

次のような方法を使って、自分でボイラープレートコードの多くを救うことができるcloseQuietlyDBUtilsアパッチから。


1
ありがとうございました。ポイントは、Connection.closeの実装に依存できないということです。
Zeemee


39

現在、JavaでOracleを使用しています。ここに私の視点:

Oracleでは、接続を閉じた後でもカーソルを開いたままにしておくことに以前問題があったためResultSetStatement明示的に閉じる必要があります。ResultSet(カーソル)を閉じないと、[ 開いているカーソルの最大数を超えました]などのエラーがスローされます。

使用している他のデータベースでも同じ問題が発生する可能性があります。

チュートリアルが終了したら、ResultSetを閉じます

終了したらResultSetを閉じる

ResultSetオブジェクトを閉じるときに暗黙的にオブジェクトを閉じるResultSet場合でも、Statementオブジェクトの操作が完了するとすぐにオブジェクトを閉じ ます。クエリによってはオブジェクトが大量のメモリを占有する可能性があるためResultSet、閉じるとResultSet明示的にガベージコレクターがメモリを再収集する機会が与えResultSetられます。

ResultSet.close();


hilal氏に感謝します。これらは、できるだけ早くクローズするのに十分な理由です。ただし、ResultSetとStatementがConnectionの前に直接閉じられているかどうかは重要ですか(これは、場合によっては、できるだけ早くではないことを意味します)。
Zeemee

接続を閉じると、すべての結果セットとステートメントが閉じられますが、接続前に結果セットを閉じる必要があります

接続する前に結果セットを閉じる必要があるのはなぜですか?オラクルドライバーの問題が原因ですか?
Zeemee

1
ここではより一般的な解明:)ですstackoverflow.com/questions/103938/...

理論的には、ステートメントを閉じる場合、結果セットを閉じる必要はありません、おそらく良い方法です。
rogerdpack 2013年

8

よりコンパクトなコードが必要な場合は、Apache Commons DbUtilsを使用することをお勧めします。この場合:

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(stmt);
    DbUtils.closeQuietly(conn);
}

3
rs.close()、stmt.close()、conn.close()の代わりにこのコードを使用するとどうなりますか
Onkar Musale

3

JDBCに関連付けられているリソースを閉じるための正しい安全な方法(JDBCリソースを適切に閉じる方法から-毎回取得):

Connection connection = dataSource.getConnection();
try {
    Statement statement = connection.createStatement();

    try {
        ResultSet resultSet = statement.executeQuery("some query");

        try {
            // Do stuff with the result set.
        } finally {
            resultSet.close();
        }
    } finally {
        statement.close();
    }
} finally {
    connection.close();
}

3

Connectionがプール可能かどうかは関係ありません。プール可能な接続でも、プールに戻る前にクリーンアップする必要があります。

「クリーン」とは通常、結果セットを閉じ、保留中のトランザクションをロールバックしますが、接続は閉じません。さもなければ、プーリングはその意味を失います。


2

いいえ、接続を閉じる必要はありません。JDBC仕様によると、上位のオブジェクトを閉じると、下位のオブジェクトも自動的に閉じます。を閉じるConnectionStatement、接続が作成したがすべて閉じられます。any Statementを閉じると、ResultSetそれによって作成されたすべてのが閉じますStatementConnectionがプール可能かどうかは関係ありません。プール可能な接続でも、プールに戻る前にクリーンアップする必要があります。

もちろん、Connectionたくさんのステートメントを作成する際に長いネストされたループがあるかもしれませんが、それらを閉じることが適切です。私はほとんど決して近いResultSetけれども、閉じるときに過度思わStatementまたはConnectionそれらを閉じます。


1

再利用可能なワンライナーを作成するために、次のメソッドを作成しました。

public void oneMethodToCloseThemAll(ResultSet resultSet, Statement statement, Connection connection) {
    if (resultSet != null) {
        try {
            if (!resultSet.isClosed()) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            if (!statement.isClosed()) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if (connection != null) {
        try {
            if (!connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

このコードは、DBクエリを送信するすべてのクラスに継承される親クラスで使用します。結果セットがない場合でも、すべてのクエリでOnelinerを使用できます。メソッドは、ResultSet、Statement、Connectionを正しい順序で閉じる処理を行います。これが最終的に私のブロックがどのように見えるかです。

finally {
    oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}

-1

私が覚えている限りでは、現在のJDBCでは、ResultsetsとステートメントはAutoCloseableインターフェースを実装しています。つまり、破棄されるか範囲外になると、自動的に閉じられます。


3
いいえ、それcloseはtry-with-resourcesステートメントの終了時にが呼び出されることを意味するだけです。docs.oracle.com/javase/tutorial/essential/exceptions/…およびdocs.oracle.com/javase/8/docs/api/java/lang/Au​​toCloseable.htmlを参照してください。
Zeemee

-1

いくつかの便利な機能:

public static void silentCloseResultSets(Statement st) {
    try {
        while (!(!st.getMoreResults() && (st.getUpdateCount() == -1))) {}
    } catch (SQLException ignore) {}
}
public static void silentCloseResultSets(Statement ...statements) {
    for (Statement st: statements) silentCloseResultSets(st);
}

ここで何かを閉じるものはありません。明らかに不要であっても、応答全体を無駄に読み取る無意味なループです。
ローン侯爵

-1

Java 6フォームでは、閉じる前に閉じるかどうかを確認することをお勧めします(たとえば、一部の接続プーラーが他のスレッドで接続を排除する場合)-たとえば、いくつかのネットワークの問題-ステートメントと結果セットの状態が閉じる可能性があります。(それは頻繁には起こりませんが、OracleとDBCPでこの問題がありました)。私のパターンは(古いJava構文で)そのためです:

try {
    //...   
    return resp;
} finally {
    if (rs != null && !rs.isClosed()) {
        try {
            rs.close();
        } catch (Exception e2) { 
            log.warn("Cannot close resultset: " + e2.getMessage());
        }
    }
    if (stmt != null && !stmt.isClosed()) {
        try {
            stmt.close();
        } catch (Exception e2) {
            log.warn("Cannot close statement " + e2.getMessage()); 
        }
    }
    if (con != null && !conn.isClosed()) {
        try {
            con.close();
        } catch (Exception e2) {
            log.warn("Cannot close connection: " + e2.getMessage());
        }
    }
}

理論的には、クローズ状態のチェックとクローズ自体のチェックの間に状態の変化の余地があるため、100%完全ではありません。最悪の場合、長い間警告が表示されます。-しかし、長期実行クエリで状態が変化する可能性よりも低くなります。このパターンは「アバレッジ」ロード(同時ユーザー150人)で本番環境で使用しており、問題はありませんでした。そのため、警告メッセージは表示されません。


isClosed()テストは必要ありません。すでに閉じられているものを閉じると何も起こらないためです。これにより、タイミングウィンドウの問題が解消されます。これはConnection, Statement、とResultSetローカル変数を作成することによっても削除されます。
ローン侯爵
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.