1

如果我有以下代码,这是否是关闭 Connection、ResultSet 和 Statement 对象的正确方法?我觉得所有的调用都close()应该在 finally 块中。

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

4 回答 4

4

是的,关闭资源应该在一个finally块中,因为无论哪里可能引发异常,您都应该关闭所有资源。

标准模式是:

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
}

尽管未能关闭其中一个资源可能意味着其他资源也会爆炸,但尝试关闭每个资源仍然是一种好习惯,因此在 finally 块中单独尝试捕获。

于 2013-07-08T14:47:02.307 回答
3

在 Java 7 中,您可以使用try-with-resource语句:

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

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

在 Java 6 中,您可以关闭finally块中的资源:

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

您甚至可以使用辅助类来关闭连接。Apache Commons DbUtils有一个DbUtils类。

} finally {
    DbUtil.closeQuietly(rs);
    DbUtil.closeQuietly(ps);
    DbUtil.closeQuietly(conn);
}
于 2013-07-08T14:45:49.310 回答
3

您不需要Statement在循环中创建:您可以重复使用它。此外,使用 Java 7 try-with-resources ( tutorial ),您无需处理以正确顺序关闭的乏味等等。

你可以这样做:

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
}

正如您所看到的,与自己管理相比,它需要的检查和语句要少得多,同时它确保以正确的顺序关闭资源,即使发生异常(即使在关闭其他资源之一时发生该异常)。例如,如果发生任何异常,您的旧代码无法关闭资源。

于 2013-07-08T14:53:17.093 回答
0

我建议使用嵌套的 try/catch 块,每个资源一个:

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

每个 finally 子句中只有一行。否则,您必须将其包装在另一个 try/catch 中,以便在 finally 块中正确处理可能的异常。


一般来说,这种管理资源的模式(try/catch - 是否嵌套)会使代码变得混乱。摆脱的一种方法是集中管理您的资源,并使用监听器/阅读器。

所以在一个库中你定义了一个Reader和一个with...方法:

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

像这样在调用站点与匿名类一起使用

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

这样在使用站点就没有try/catch,没有人忘记关闭资源(也没有人错误地关闭资源)。

如果 Java 8 可用,则可以将ResultSetReader接口替换为闭包。

于 2013-07-08T15:39:24.740 回答