How to properly dispose of Connection, ResultSet,

2019-07-30 08:13发布

问题:

If i have the following code would this be the correct way of closing the Connection, ResultSet, and Statement objects? I feel like all of the calls to close() should be in a finally block.

Connection con = null;
ResultSet rs = null;
Statement stmt = null;

try{
    //Code before the while loop 
    con = DriveManager.getConnection("Stuff");

    while(someBoolean){          

        stmt = con.createStatement();
        rs = stmt.executeQuery("SQL query");

        // do stuff with query results.

        if( rs != null){
               rs.close();
        }

        if( stmt != null){
               stmt.close();
        }

} //end while

    if( con != null ){
        con.close();
    }

catch (Exception e){
    //handle exception
}

回答1:

You don't need to create the Statement in the loop: you can re-use it. Also with Java 7 try-with-resources (tutorial) you don't need to take care of the tedium of closing in the right order, etc.

You can do that like this:

try (
    Connection con = DriverManager.getConnection("Stuff");
    Statement stmt = con.createStatement();
){
    while(someBoolean){          
        try (ResultSet rs = stmt.executeQuery("SQL query")) {
            // do stuff with query results.
        }
    } //end while
} catch (Exception e){
    //handle exception
}

As you can see it requires a lot less checks and statements than managing it yourself, while it ensures closing the resources in the right order, even if exceptions occur (even if that exception occurs when closing one of the other resources). For example your old code failed to close resources if any exception occurred.



回答2:

Yes, closing resources should be in a finally block, because you should close all resources no matter where an exception might be thrown.

The standard pattern is:

Connection con = null;
ResultSet rs = null;
Statement stmt = null;

try {
    con = DriveManager.getConnection("Stuff");
    stmt = con.createStatement();
    rs = stmt.executeQuery("SQL query");
    // do stuff with query results
} catch (SQLException e) { // Don't catch Exception, catch what you expect
    // handle exception
} finally {
    // each close can itself explode, so wrap each in try catch
    try {
       if (rs != null)
           rs.close();
    } catch (SQLException ignore) {} // no point handling

    try {
       if (stmt != null)
           stmt.close();
    } catch (SQLException ignore) {} // no point handling

    try {
       if (con != null)
           con.close();
    } catch (SQLException ignore) {} // no point handling
}

Although a failure to close one of the resources probably means the others will explode too, it;'s still good practice to attempt to close each, hence the individual try-catches inside the finally block.



回答3:

In Java 7 , you can use try-with-resource statement :

try(Connection con = getConnection(url, username, password, "drivername");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(sql);
) {

  //statements
}catch(....){}

In Java 6 , you can close the resources in finally block :

} finally {
   try { rs.close(); } catch (Exception e) {  }
   try { ps.close(); } catch (Exception e) {  }
   try { conn.close(); } catch (Exception e) {  }
}

You can even use helper class to close the connection . Apache Commons DbUtils has a DbUtils class.

} finally {
    DbUtil.closeQuietly(rs);
    DbUtil.closeQuietly(ps);
    DbUtil.closeQuietly(conn);
}


回答4:

I suggest having nested try/catch blocks, one for each resource:

    final Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser,
            jdbcPassword);
    try {
        final Statement stmnt = conn.createStatement();
        try {
            final ResultSet rs = stmnt
                    .executeQuery("select * from pg_user");
            try {
                while (rs.next()) {
                    System.out.println(rs.getObject(1));
                }
            } finally {
                rs.close();
            }
        } finally {
            stmnt.close();
        }
    } finally {
        conn.close();
    }

There is only one single line in each finally clause. Otherwise you have to wrap it in another try/catch, so that possible exceptions are handled correctly in a finally block.


Generally speaking, this pattern (try/catch - nested or not) to manage resources clutters up the code. One method to get rid of is to manage your resources centrally, and use a listener/reader.

So in a library you define a Reader and a with... method:

interface ResultSetReader {
    void read(ResultSet rs) throws SQLException;
}

public static void withResultSet(final String query,
        final ResultSetReader reader) throws Exception {
    final Connection conn = getConnection();
    try {
        final Statement stmnt = conn.createStatement();
        try {
            final ResultSet rs = stmnt.executeQuery(query);
            try {
                while (rs.next()) {
                    reader.read(rs);
                }
            } finally {
                rs.close();
            }
        } finally {
            stmnt.close();
        }
    } finally {
        conn.close();
    }
}

to be used with an anonymous class at the calling site like this

    withResultSet("select * from pg_user", new ResultSetReader() {
        @Override
        public void read(ResultSet rs) throws SQLException {
            System.out.println(rs.getObject(1));
        }
    });

This way there is no try/catch at the usage site, and nobody forgets to to close resources (and nobody closes resources incorrectly).

If Java 8 is available, the ResultSetReader interface could by replaced by a closure.



标签: java jdbc