21

As I was cleaning up some code, FindBugs pointed me to a bit of JDBC code that uses Connection, CallableStatement, and ResultSet objects. Here's a snippet from that code:

CallableStatement cStmt = getConnection().prepareCall("...");
...
ResultSet rs = cStmt.executeQuery();

while ( rs.next() )
{
    ...
}

cStmt.close();
rs.close();
con.close();

FindBugs pointed out that these should be within a finally block. I started refactoring my code to do this and I started to wonder how to handle the code within the finally block.

It's possible that the creation of the CallableStatement of Connection objects would throw an exception, leaving my ResultSet object as null. When I try to close the ResultSet, I'd get a NullPointerException and my Connection would, in turn, never get closed. Indeed, this thread brings up the same concept and shows that wrapping your close() invocations in a null check is a good idea.

But what about other possible exceptions? According to the Java API spec, Statement.close() can throw a SQLException "if a database error occurs". So, even if my CallableStatement is not null and I can successfully call close() on it, I might still get an exception and not have a chance to close my other resources.

The only "fail safe" solution I can think of is to wrap each close() invocation in its own try/catch block, like this:

finally {

    try {
        cStmt.close();
    } catch (Exception e) { /* Intentionally Swallow  Exception */ }

    try {
        rs.close();
    } catch (Exception e) { /* Intentionally Swallow  Exception */ }

    try {
        con.close();
    } catch (Exception e) { /* Intentionally Swallow  Exception */ }

}

Boy, if that doesn't look awful. Is there a better way to go about this?

Community
  • 1
  • 1
McGlone
  • 3,434
  • 5
  • 26
  • 31

6 Answers6

11

I think the best answer has already being mentioned, but I thought it could be interesing to mention that you could consider the new JDK 7 feature of autoclosable resources.

try{
    try(Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/hrdb", "obiwan", "kenobi"); 
        Statement stm = conn.createStatement(); 
        ResultSet rs = stm.executeQuery("select name from department")) {

        while(rs.next()){
            System.out.println(rs.getString("name"));
        }

    } 
}catch(SQLException e){
    //you might wanna check e.getSuppressed() as well
    //log, wrap, rethrow as desired.
}

Not all of us can migrate to JDK 7 now, but for those who can start playing with the developer preview, this offer an interesting way of doing things and certainly may deprecate other approaches in the near future.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
5

Use Lombok's cleanup, if you can:

@Cleanup
Connection c = ...
@Cleanup
statement = c.prepareStatement(...);
@Cleanup
rs = statement.execute(...);

This works translates to three nested try-finally blocks and works fine with exception. Never ever swallow an exception without a very good reason!

An alternative:

Write an own utility method like this:

public static void close(ResultSet rs, Statement stmt, Connection con) throws SQLException {
    try {
        try {
            if (rs!=null) rs.close();
        } finally {
            if (stmt!=null) stmt.close();
        }
    } finally {
        if (con!=null) con.close();
    }
}

and use it in

try {
    Connection con = ...
    Statement stmt = ...
    ResultSet rs = ...
} finally {
    close(rs, stmt, con);
}

and let the Exception bubble up or handle it as you want.

Community
  • 1
  • 1
maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • Your alternative really isn't all that different from MJB's idea, I think the progression from calling close() on one resource to adding three in the method definition is pretty obvious - and for this you downvoted his answer? – matt b Apr 25 '11 at 20:31
  • I downvoted his answer only because of swallowing exceptions - this is something nobody should do in an example presented publicly. Because of seeing it in examples, beginners do it again and again.... and you explain it over and over. His answer can't be modified so it doesn't swallow exceptions - that's the main difference. – maaartinus Apr 25 '11 at 20:45
  • 2
    @maaartinus I have to say that in MJB code excerpt exceptions are not being swallowed, they are being logged, which is something totally different and strategically valid, above all for an exception occurring in a close method where you most probably will not want to do anything to recover, IMHO. – Edwin Dalorzo Apr 25 '11 at 20:57
  • @edalorzo Yes, logging is *sometimes* the way to handle exceptions. *If it was always applicable for close methods, there'd be no exception but just log statements in the JDBC*. Sometimes logging is as bad as doing nothing. – maaartinus Apr 25 '11 at 21:03
  • 1
    My point is that I cannot see how your approach is better than MIJBs. You provide a static close method for which you still need to handle the exception in question (if it happens), but you have not yet shown how to deal with the issue of closing the resource. In MIJ's approach, he simply logs the exception and assumes there is nothing to do regarding closing the resource. In your approach, you simply forward the exception up the call stack. According to you, your approach is better because you can do something about it. That's arguably right until we can prove we can do something about it. – Edwin Dalorzo Apr 25 '11 at 21:54
  • @edalorzo Agreed, I haven't shown how to deal with it, since it's probably not the job of this code snippet. In 99% of cases, declaring `throws SQLException` is the way to go, leaving the problem purportedly open. IMHO, not doing (possibly wrongly) what I wasn't ask to is the right thing, but I see your point. – maaartinus Apr 25 '11 at 22:03
3

Well basically that is what you do except you first of all don't necessarily swallow the exception (you can null check and at least LOG the exception). Second, you can set up a nice utility class with things like

public static void close(ResultSet rs) {
   try { if (rs != null) rs.close();
   } catch (SQLException (e) {
      log.error("",e);
   } 

}

Then you just static import that class.

your finally becomes something like

finally {
     close(resultset);
     close(statement);
     close(connection);
}

which really isn't that hideous.

MJB
  • 9,352
  • 6
  • 34
  • 49
  • 2
    -1 You're basically swallowing exceptions. At this place you have no chance to handle them since you don't know where it gets used. Logging != handling (although sometimes appropriate). – maaartinus Apr 25 '11 at 20:16
  • I considered this, but the problem is that I'd need to have a utility method for every type of object I wanted to close. In my case, that would be three methods that are identical except for the parameter type. To get around that, I could use reflection to find a method called "close" and invoke it, but I don't see how that makes things any better. – McGlone Apr 25 '11 at 20:16
  • This still looks fairly hideous. You could also create a parent close method that will take a ResultSet, Statement and Connection and will then call close on each of them. Going from three close lines to 1. – kfox Apr 25 '11 at 20:20
  • @kfox That's what I did in my answer. @McGlone There's an interface `Autocloseable` in JDK7 declaring throws Exception and designed probably for this reason. – maaartinus Apr 25 '11 at 20:28
  • cglone,kfox - Judgment call here. My theory is you probably have more than one jdbc usage in your program. I guess you could come up with every permutation of statement/preparedstatement/resultset/connection, I think it's clearer this way. Maatinus - if you've logged your exception,that's usually all you can do with SQL. The example you gave with bank update is invalid, because normally you'd do a setAutoCommit(false), and then do COMMIT, followed by a ROLLBACK if there's an exception. You would never rely on the CLOSE of the statement/resultset/connection to commit the change. (-40 if you do) – MJB Apr 26 '11 at 05:53
  • Only think that there are a lot of programmers that develop in JDK6 yet. This method is clean to close your resources but you can loose your real exceptions. Anyway, better than resultsets I think you should use a more generic type (Closeable) – Genaut Dec 27 '16 at 08:59
3

You only have to close the Connection.

try
{
    cStmt.close();
}
catch(Exception e)
{
    /* Intentionally Swallow Exception */
} 

From docs.oracle.com:

A Statement object is automatically closed when it is garbage collected. When a Statement object is closed, its current ResultSet object, if one exists, is also closed.

Calling close() on a Connection releases its database and JDBC resources.

Roger C S Wernersson
  • 6,362
  • 6
  • 34
  • 45
2

The only way I know to hide all that ugly try-catch boilerplate code is to utilize something like Spring's JBDC Template.

limc
  • 39,366
  • 20
  • 100
  • 145
-2

You can wrap one block into another block:

try{
  Connection c = ...
    try{
      statement = c.prepareStatement(...);
      try{
        rs = statement.execute(...);
      }finally{
        rs.close();
      }
    }finally{
      statement.close()
    }
  }finally{
    c.close();
  }
}catch(SQLException e){}

Use the lowermost catch-block for everything that might crop up

kkiefer
  • 143
  • 2
  • 11
  • 3
    @maaartinus - there is almost no practical value in re-throwing an exception caught on closing of a resource. – matt b Apr 25 '11 at 20:30
  • 1
    Moreover, the structure is wrong or null checks are missing. You may get a NullPointerException for both 'rs` and `statement`. – maaartinus Apr 25 '11 at 20:31
  • 1
    @matt b: I agree, that there's not much you could do about it, but you can't just ignore it. In case there was any UPDATE in the transaction, you have to deal with the possibility for the data not being written. Imagine it was a bank transaction... – maaartinus Apr 25 '11 at 20:35