9

What is considered best practices when cleaning up JDBC resources and why? I kept the example short, thus just the cleaning up of the ResultSet.

finally
{
  if(rs != null)
    try{ rs.close(); } catch(SQLException ignored) {}
}

versus

finally
{
  try{ rs.close(); } catch(Exception ignored) {}
}

Personally I favour the second option since it is a bit shorter. Any input on this is much appreciated.

Alexander Rosemann
  • 131
  • 1
  • 1
  • 4
  • Never tought about the second option - it can be useful for closing streams as well. BTW - I like your name for the `ignored` exception and will keep that in mind :-) – Andreas Dolk Dec 22 '10 at 10:33
  • Anyway, forget about "keeping the example short" :-) – Costis Aivalis Dec 22 '10 at 11:14
  • I would recommend Vickie's reply below. It is never really good practice to expect an exception to be thrown as part of the normal flow of code. In your second, preferred block, you're relying on the exception to handle the case of a null ResultSet instead of checking it yourself. – ndurante Oct 16 '15 at 14:38

9 Answers9

11

Nowadays JDK 7 gives you the easiest option to clean up resources:

String query = "select COF_NAME, PRICE from COFFEES";
try (Statement stmt = con.createStatement()) {
    ResultSet rs = stmt.executeQuery(query);
    while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        float price = rs.getFloat("PRICE");
        System.out.println(coffeeName + ", "  + price);
    }
}

The try statement ensures that each resource is closed at the end of the statement. See http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

André
  • 12,497
  • 6
  • 42
  • 44
4

As others have pointed out, JDBC resources (statements, result sets, etc...) are rarely null. If they are, you have bigger issues on your hands than NullPointerExceptions. In that regard, the NullPointerExceptions will help alert you to severe problems with your JDBC driver. The typical checking for null before calling close() would silently hide the problem if your JDBC driver was, in fact, providing you with null references.

As well, not all JDBC drivers follow the specification precisely. For example, some drivers will not automatically close a ResultSet when it's associated Statement is closed. Therefore, you have to ensure that you explicitly close both the ResultSet and its Statement (sigh).

In practice, I have found this technique useful (although its not the prettiest):

PreparedStatement statement = connection.prepareStatement("...");
try {
    ResultSet results = statement.executeQuery();
    try {
        while (results.next()) {
            // ...
        }
    } finally {
        results.close();
    }
} finally {
    statement.close();
}

This technique guarantees that every close() statement is executed, starting with the ResultSet and working its way outward. NullPointerExceptions are still thrown should the driver provide you with null references, but I allow this for the reasons explained at the beginning. SQLExceptions are still thrown if any of the close() statements fail (I consider this a good thing - I want to know if something is going wrong).

Adam Paynter
  • 46,244
  • 33
  • 149
  • 164
  • 1
    Of course you can hide this with the Execute Around idiom. Or use an ORM. – Tom Hawtin - tackline Dec 22 '10 at 11:28
  • 2
    @Tom: True. I rather like Spring's `JdbcTemplate` for this type of stuff. Although, truth be told, I actually *like* the syntax in my answer (I claim "it's not the prettiest" based on opinions I have heard from others). I find it beautiful in its simplicity and structure. Strange, but true. – Adam Paynter Dec 22 '10 at 16:52
  • How do you handle SQLExceptions thrown by close() methods? – André Jan 17 '12 at 17:43
  • @André: I purposely don't handle those `SQLException`s because I prefer to know if the driver is experiencing trouble. It's a matter of personal preference. – Adam Paynter Jan 17 '12 at 19:07
3

I see no problem with your second (uncommon) version.

  • usually, rs will not be null, so an NPE will occur in rare cases. So I see no performance problem here.
  • both version behave exactly the same in case of rs = null

The only disadvantage - if we have more then one resource to close, then we'd have to add one try/catch for each resource, if we want to close as many resources as possible. Otherwise, we'd enter the catch clause with the first null and that could cause undiscored leaks.

So it would look like that:

finally {
   try{rs.close();  }catch(Exception ignored){}
   try{stmt.close();}catch(Exception ignored){}
   try{conn.close();}catch(Exception ignored){}
}

... which is still readable and understandable. But, according to never change a common pattern - I'd stick to the old-fashioned way of testing null first and catching SQLException while closing.

Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
  • 1
    +1 for not doing something stupid with `null`s. -1 for your exception handling, **and for appearing a bit confused as to how three locals will have been initialised**. – Tom Hawtin - tackline Dec 22 '10 at 11:17
  • @Tom - this is just an assessment of the consequences implied by the OPs suggestion (version 2). Thought it would be clear enough... – Andreas Dolk Dec 22 '10 at 12:24
  • Even if it were okay to overlook such crimes, you have coalesced `finally` blocks. `finally` blocks do not coaslesce. – Tom Hawtin - tackline Dec 22 '10 at 13:15
  • that's exactly how I do it. I don't do any logging in the 'finally' catch blocks. I tend to add the logging in the catch blocks just before the finally block. – Alexander Rosemann Dec 22 '10 at 14:46
2
public static void close(Statement... statements) {
        for (Statement stmt : statements) {
            try {
                if (stmt != null)
                    stmt.close();
            } catch (SQLException se) {
            }// nothing we can do
        }
    }

    public static void close(Connection conn) {
        try {
            if (conn != null)
                conn.close();
        } catch (SQLException se) {
        }// nothing we can do
    }
public static void close(ResultSet rs) {
        try {
            if (rs != null) 
                rs.close();
        } catch (SQLException se) {
        }// nothing we can do
}
Vickie
  • 21
  • 1
2

I tend to use the following approach. I think it is good to check for null because it shows your intent i.e. that you do realise that these objects could be null in rare cases. (A null check is also faster than the creation of a NullPointerException.) I also think it is good to log the exceptions, instead of swallowing them. In the cases where close fails, I want to know about it and have it in my log files.

finally {            
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                 LOG.warn("Failed to close rs", e);
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) { 
                 LOG.warn("Failed to close st", e);     
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                 LOG.warn("Failed to close conn", e);
                }
            }
        }

If you are going to be doing this frequently, instead of copying and pasting this code over and over again, create a utility class with static methods to close the ResultSet, Statement and Connection.

With DBUtils you can perform this cleanup quite concisely as follows:

 finally {            
            DBUtils.closeQuietly(rs);
            DBUtils.closeQuietly(st);
            DBUtils.closeQuietly(conn);            
        }
dogbane
  • 266,786
  • 75
  • 396
  • 414
  • Oh dear. Something daft with `null`s and swallowing exceptions. – Tom Hawtin - tackline Dec 22 '10 at 11:18
  • 1
    This is brilliant solution as Tom stated thats a check for null is cheaper than throwing a NullPointerException. Logging is optional in my opinion, especially by a close. Who cares if it fails to close, if it was no successful then odds are your pooling is not going to reuse the connection anyway etc. – Paul Dec 22 '10 at 16:21
1

This is my approach for JDK 6. If you have JDK 7+ you better use the approach I describe here https://stackoverflow.com/a/9200053/259237

private void querySomething() {
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet rs = null;
    try {
        // get connection
        // prepare statement
        // execute query
        // and so on
    } catch (SQLException e) {
        throw new MyException("Error while talking to database", e);
    } finally {
        close(connection, statement, rs);
    }
}

// useful because you probably have more than one method interacting with database
public static void close (Connection connection, Statement statement, ResultSet rs) {
    if (rs != null) {
        try { rs.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
    if (statement != null) {
        try { statement.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
    if (connection != null) {
        try { connection.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
}
  • It's short.
  • It defines a close method that can be statically imported.
  • It avoids empty catch blocks.
  • It handles any SQLException that may occur (even in getConnection or close methods).
  • It's null-safe.
Community
  • 1
  • 1
André
  • 12,497
  • 6
  • 42
  • 44
0
ResultSet rs = //initialize here
try {
  // do stuff here
} finally {
  try { rs.close(); }
  catch(SQLException ignored) {}
}
khachik
  • 28,112
  • 9
  • 59
  • 94
0

If you are writing a long running application you should consider connection pooling.

The Apache DBCP project does a lot of this work for you. You could also look at something like Spring JDBC or Hibernate as well.

The Spring stuff uses object pooling and adds some really nice methods for abstracting away JDBC nastiness.

Fortyrunner
  • 12,702
  • 4
  • 31
  • 54
  • What does this answer have to do with the question? If using database connection pooling, it becomes even *more* important to be sure you close all the resources (statements and result sets) created by the current connection so you don't put a dirty connection back into the pool. – Basil Bourque Apr 03 '13 at 07:09
  • fair point about DBCP - you do still need to clean up. But combine it with the Spring framework (using the JDBCTemplate) and you can remove a lot of boiler plate code. – Fortyrunner Apr 04 '13 at 17:48
0
protected void closeAll(){
        closeResultSet();
        closeStatement();
        closeConnection();
    }   

protected void closeConnection(){
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                /*Logger*/
            }
            connection = null;
        }
    }


    protected void closeStatement() {
        if (stmt != null) {
            try {
                ocstmt.close();
            } catch (SQLException e) {
                /*Logger*/
            }
            ocstmt = null;
        }
    }


    protected void closeResultSet() {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                /*Logger*/
            }
            rs = null;
        }
    }
  • you can use Logger before closing each. I assume you know this but one more time http://download.oracle.com/javase/1.4.2/docs/api/java/util/logging/Logger.html –  Dec 22 '10 at 11:14
  • -1 Worst yet. `null` silliness. Swallowing exceptions. And a lack of `finally`. – Tom Hawtin - tackline Dec 22 '10 at 11:19
  • Item 7: Avoid Finalizers (Effective Java) –  Dec 22 '10 at 11:24
  • http://books.google.com/books?id=ka2VUBqHiWkC&printsec=frontcover&dq=effective+java&hl=en&src=bmrr&ei=_N8RTeL8Jo_rOfrq5N8I&sa=X&oi=book_result&ct=result&resnum=1&ved=0CCYQ6AEwAA#v=onepage&q&f=false –  Dec 22 '10 at 11:25
  • I know it is differ from final and thanks to your for(;;){print('!')} –  Dec 22 '10 at 11:31
  • @hilal: I believe Tom misspelled `finally` when he said "Finalisers are a completely different thing to `final`!!!!!!!111!1!!". You responded by saying that you "know it is differ from final". Just so we're clear, do you know the difference between *finalizers* and *`finally`*? I'm a bit confused by the conversation. :) – Adam Paynter Dec 22 '10 at 11:53
  • @Tom agression never give results –  Dec 22 '10 at 11:54
  • I just just want to show that Java architects avoid from using finally. Anyway, I commented too much. sorry for that, my friends –  Dec 22 '10 at 11:56
  • @hilal: I think you are a little confused. *Finalizers* are when you override the `finalize()` method of the `Object` class. They are considered bad because you're not guaranteed they will ever be called. However, `finally` statements are part of the try/catch statement that Java experts use regularly. Their names are similar, so they're easy to confuse. – Adam Paynter Dec 22 '10 at 12:07
  • @hilal putting the code to clean up in separate methods is ok but I would rather spend my time implementing the template method pattern. – Alexander Rosemann Dec 22 '10 at 14:56