298

It is said to be a good habit to close all JDBC resources after usage. But if I have the following code, is it necessary to close the Resultset and the Statement?

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) {};
}

The question is if the closing of the connection does the job or if it leaves some resources in use.

Zeemee
  • 10,486
  • 14
  • 51
  • 81
  • Possible duplicate of [Closing Database Connections in Java](https://stackoverflow.com/questions/2225221/closing-database-connections-in-java) – Austin Schaefer Jul 27 '17 at 14:54

11 Answers11

234

What you have done is perfect and very good practice.

The reason I say its good practice... For example, if for some reason you are using a "primitive" type of database pooling and you call connection.close(), the connection will be returned to the pool and the ResultSet/Statement will never be closed and then you will run into many different new problems!

So you can't always count on connection.close() to clean up.

starball
  • 20,030
  • 7
  • 43
  • 238
Paul
  • 4,812
  • 3
  • 27
  • 38
  • 5
    ... and the most evident reason to close everything explicitly. – Zeemee Dec 23 '10 at 09:25
  • 2
    I agree that it is good practice to close result sets and statements. However, the result sets and statements are garbage collected - they don't stay open forever and you don't "run into many different new problems". – stepanian Sep 07 '11 at 09:16
  • 3
    @Ralph Stevens - You cannot count on that. I have had a situation where the MSSQL JDBC driver leaked memory because the ResultSet's were not closed, even after being garbage collected. – Paul Sep 08 '11 at 05:52
  • 7
    @Paul - Interesting. That sounds to me like a shortcoming of the JDBC driver. – stepanian Sep 09 '11 at 20:31
  • @Paul Can you point us to the problems that might come up, a link would be fine. Is the leaked memory the only one? – giannis christofakis Mar 01 '16 at 11:09
  • If you're using Java7+ you can also create a method with a AutoClosable parameter. Will be something like this: void closeResource(AutoCloseable resource) { if (resource != null) { try { resource.close(); } catch (Exception e) { } } } or use [try-with-resources](http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) – Tiago Mussi Aug 31 '16 at 19:19
  • Just a little question: wouldn't it be valid to remove the `if` statements? The catch should handle the case, no? Or is it for a performance reason? – tleb Oct 15 '16 at 15:16
  • 2
    @tleb - that would work as expected. although in theory, exceptions are "expensive" so there would be a very small performance knock (which you have already identified) – Paul Oct 16 '16 at 18:08
  • 1
    there is no point in closing result set if you close underline statement, no? Result sets are always bound to statement and close will close them all... – Enerccio Mar 16 '18 at 11:57
  • 1
    also saying that closing pooling connection will leak is a lie – Enerccio Jul 04 '18 at 05:55
  • A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results. - says [javadoc](https://docs.oracle.com/javase/6/docs/api/java/sql/ResultSet.html) – Meet Patel Aug 02 '19 at 12:43
  • 1
    I would expect that `Statement.close()` would close all `Statements` and `ResultSets` even in a pooled implementation, and I would consider any such pool that didn't do that to be non-JDBC-compliant. – user207421 Jun 11 '20 at 10:00
  • Never be closed? They eventually be garbage collected right? – Gayan Weerakutti Jan 23 '22 at 09:19
142

Java 1.7 makes our lives much easier thanks to the try-with-resources statement.

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.
    }
}

This syntax is quite brief and elegant. And connection will indeed be closed even when the statement couldn't be created.

Christian Hujer
  • 17,035
  • 5
  • 40
  • 47
Raúl Salinas-Monteagudo
  • 3,412
  • 1
  • 24
  • 22
  • 65
    You don't need to nest like this, you can do it all in one try-with-resources, just treat the resource declarations as separate statements (separated by `;`) – Mark Rotteveel Aug 19 '13 at 07:59
  • 3
    Mark Rotteveel: you can use a single try for all three Connection, Statement and ResultSet, but if you want to perform several queries, you must close the previous ResultSet before starting a new query. At least that is the way how the DBMS I was using worked. – Raúl Salinas-Monteagudo Oct 17 '15 at 13:39
  • why won't you do something like this ? try(open connection){ try(multiple statements & resultsets){ especially when next queries results can compute with previous ones. – Daniel Hajduk Nov 26 '15 at 11:07
  • Daniel: When I used that pattern, the underlying JDBC backend did not support keeping a ResultSet open and open a second one. – Raúl Salinas-Monteagudo Nov 27 '15 at 11:27
  • rascio, you can do whatever you need in the catch block – Raúl Salinas-Monteagudo Mar 08 '16 at 16:58
  • @Mark Rotteveel if you use Oracle you still need to have nested try – gstackoverflow Apr 09 '21 at 14:06
  • @gstackoverflow My comment was on [an earlier version](https://stackoverflow.com/revisions/15728512/1) of this answer where three levels of try-with-resources was used instead of the current two. And whether or not to use nested tries has nothing to do with the database system used. – Mark Rotteveel Apr 09 '21 at 14:59
  • @Mark Rotteveel, could you please clarify this part: `And whether or not to use nested tries has nothing to do with the database system used` It is quite famous fact about Oracle: https://stackoverflow.com/a/4507507/2674303 – gstackoverflow Apr 09 '21 at 18:11
  • 1
    @gstackoverflow That is an entirely different problem, I was commenting on the fact that this answer originally had three levels of nested try-with-resource, while the first two levels could be combined into a single try-with-resources by specifying both the connection and the statement in the same resource definition. In other words, as the answer is now. – Mark Rotteveel Apr 10 '21 at 06:05
  • statement needs to be closed as well. This "good practice" will actually cause a leak if the case mentioned in the previous answer, where connection close does not close owning statements and resultSets. – Razvan P Jan 21 '22 at 13:21
79

From the javadocs:

When a Statement object is closed, its current ResultSet object, if one exists, is also closed.

However, the javadocs are not very clear on whether the Statement and ResultSet are closed when you close the underlying Connection. They simply state that closing a Connection:

Releases this Connection object's database and JDBC resources immediately instead of waiting for them to be automatically released.

In my opinion, always explicitly close ResultSets, Statements and Connections when you are finished with them as the implementation of close could vary between database drivers.

You can save yourself a lot of boiler-plate code by using methods such as closeQuietly in DBUtils from Apache.

dogbane
  • 266,786
  • 75
  • 396
  • 414
46

I'm now using Oracle with Java. Here my point of view :

You should close ResultSet and Statement explicitly because Oracle has problems previously with keeping the cursors open even after closing the connection. If you don't close the ResultSet (cursor) it will throw an error like Maximum open cursors exceeded.

I think you may encounter with the same problem with other databases you use.

Here is tutorial Close ResultSet when finished:

Close ResultSet when finished

Close ResultSet object as soon as you finish working with ResultSet object even though Statement object closes the ResultSet object implicitly when it closes, closing ResultSet explicitly gives chance to garbage collector to recollect memory as early as possible because ResultSet object may occupy lot of memory depending on query.

ResultSet.close();

bluish
  • 26,356
  • 27
  • 122
  • 180
  • Thanks hilal, these are good reasons to close it as early as possible. However, does it matter if ResultSet and Statement are closed direcly before the Connection (this means in some cases: not as early as possible)? – Zeemee Dec 22 '10 at 09:02
  • If you close connection, it will close all the resultset ans statement also but you should close resultset before the connection –  Dec 22 '10 at 09:07
  • And why should i close the resultset before the connection? You mean because of the oracle driver problems? – Zeemee Dec 22 '10 at 09:12
  • 1
    here is more general clarification :) http://stackoverflow.com/questions/103938/resultset-not-closed-when-connection-closed –  Dec 22 '10 at 11:02
  • In theory, if you close the statement you don't *have* to close the resultsets, but it's probably good practice. – rogerdpack Nov 22 '13 at 21:45
  • What about now? – Alex78191 Apr 15 '19 at 15:46
  • Cursor in Oracle is not closed after closing resultset. I've checked it using query `select cursor_type, oc.* from v$open_cursor oc where sql_text like '%query%';` You should close statement, not resultset. – Alex78191 Jan 31 '20 at 13:58
  • Cursors are also created while executing insert statement in Oracle. – Alex78191 Jan 31 '20 at 13:59
9

If you want more compact code, I suggest using Apache Commons DbUtils. In this case:

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);
}
baron5
  • 577
  • 2
  • 11
  • 25
6

No you are not required to close anything BUT the connection. Per JDBC specs closing any higher object will automatically close lower objects. Closing Connection will close any Statements that connection has created. Closing any Statement will close all ResultSets that were created by that Statement. Doesn't matter if Connection is poolable or not. Even poolable connection has to clean before returning to the pool.

Of course you might have long nested loops on the Connection creating lots of statements, then closing them is appropriate. I almost never close ResultSet though, seems excessive when closing Statement or Connection WILL close them.

user207421
  • 305,947
  • 44
  • 307
  • 483
Enerccio
  • 257
  • 1
  • 10
  • 23
3

Doesn't matter if Connection is poolable or not. Even poolable connection has to clean before returning to the pool.

"Clean" usually means closing resultsets & rolling back any pending transactions but not closing the connection. Otherwise pooling looses its sense.

user207421
  • 305,947
  • 44
  • 307
  • 483
Mad Calm
  • 52
  • 4
2

The correct and safe method for close the resources associated with JDBC this (taken from How to Close JDBC Resources Properly – Every Time):

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();
}
bluish
  • 26,356
  • 27
  • 122
  • 180
2

I created the following Method to create reusable One Liner:

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();
        }
    }
}

I use this Code in a parent Class thats inherited to all my classes that send DB Queries. I can use the Oneliner on all Queries, even if i do not have a resultSet.The Method takes care of closing the ResultSet, Statement, Connection in the correct order. This is what my finally block looks like.

finally {
    oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}
eckad158
  • 385
  • 6
  • 19
-1

With Java 6 form I think is better to check it is closed or not before close (for example if some connection pooler evict the connection in other thread) - for example some network problem - the statement and resultset state can be come closed. (it is not often happens, but I had this problem with Oracle and DBCP). My pattern is for that (in older Java syntax) is:

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());
        }
    }
}

In theory it is not 100% perfect because between the the checking the close state and the close itself there is a little room for the change for state. In the worst case you will get a warning in long. - but it is lesser than the possibility of state change in long run queries. We are using this pattern in production with an "avarage" load (150 simultanous user) and we had no problem with it - so never see that warning message.

Miss Chanandler Bong
  • 4,081
  • 10
  • 26
  • 36
  • You don't need the `isClosed()` tests, because closing any of these that is already closed is a no-op. Which eliminates the timing window problem. Which would also be eliminated by making the `Connection, Statement`, and `ResultSet` local variables. – user207421 Aug 10 '17 at 07:11
-2

Some convenience functions:

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);
}
Pika Supports Ukraine
  • 3,612
  • 10
  • 26
  • 42
Mad Calm
  • 52
  • 4
  • There is nothing here that closes anything. Just a pointless loop that wastefully reads the entire response, even though it clearly isn't wanted any more. – user207421 Jun 11 '20 at 10:02
  • st.getMoreResults() in the loop does all the work regardless of how many (if any) results were fetched and type (INSERT / UPSERT) of the result. It's the most universal solution – Mad Calm Aug 01 '20 at 18:06
  • It's a universal solution for iterating all of the returned data without really using any of it, and further `#getMoreResults` (without flags) will only auto-close `ResultSet`, _not_ the `Statement` object in question. Your method's wording makes it seem like you're answering the wrong question. – Rogue Feb 06 '23 at 14:29
  • Correct. Setting 'Statement' to NULL etc without full iterating over it's results won't close the results and these convinience functions are about it. Work both for 'SELECT...' and 'UPDATE|INSERT|UPSERT ... RETURNING..."via additional check 'st.getUpdateCount() == -1' . – Mad Calm Apr 19 '23 at 06:33