在Java中关闭数据库连接

141

我有些困惑了。我正在阅读以下内容,来自于 Java Database Connectivity

Connection conn = DriverManager.getConnection(
     "jdbc:somejdbcvendor:other data needed by some jdbc vendor",
     "myLogin",
     "myPassword" );

Statement stmt = conn.createStatement();
try {
    stmt.executeUpdate( "INSERT INTO MyTable( name ) VALUES ( 'my name' ) " );
} finally {
    // It's important to close the statement when you are done with it
    stmt.close();
}

你不需要关闭conn连接吗?如果没有执行conn.close()会发生什么?

我正在维护一个私有 Web 应用程序,目前没有关闭这两个表单,但是真正重要的是stmt 还是conn,还是两者都重要?

该网站会间歇性地停止工作,但服务器一直报告数据库连接问题。我的猜测是它没有被关闭,但我不知道应该关闭哪一个,如果有的话。


在编程中,最佳实践始终是自己关闭连接,而不依赖其他驱动程序和模板来处理关闭。如果未能关闭连接,则会导致套接字和资源一直开放,直到崩溃(没有更多的资源场景)或重新启动。 - Arun Joseph
7个回答

224

在使用完你的Connection后,你需要通过调用其close()方法来显式地关闭它,以释放连接可能占用的任何其他数据库资源(游标、句柄等)。

实际上,在Java中的安全模式是在完成使用后,使用finally块依次关闭你的ResultSetStatementConnection。像这样:

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

try {
    // Do stuff
    ...

} catch (SQLException ex) {
    // Exception handling stuff
    ...
} finally {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) { /* Ignored */}
    }
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) { /* Ignored */}
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) { /* Ignored */}
    }
}

finally 块可以稍微改进一下(避免空检查):

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

但是,这仍然非常冗长,因此通常会使用辅助类在空值安全的辅助方法中关闭对象,而finally块将变成以下形式:

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

实际上,Apache Commons DbUtils 实现了一个 DbUtils 类,该类正是在做这件事情,因此没有必要编写你自己的代码。


3
非常感谢您的帮助!我没有注意到或想到过 "conn != null" 这个语句。 - onaclov2000
13
@Pascal Thivent:实际上我们不需要关闭所有的。《Java 核心技术 卷 II 高级特性》一书已经写道:如果语句具有打开的结果集,则 Statement 对象的 close 方法会自动关闭相关联的 ResultSet。同样,Connection 类的 close 方法会关闭该连接的所有 Statements。 - Majid Azimi
16
除非它是汇聚连接,否则语句将泄漏。 - BalusC
2
@BalusC:您能否解释一下使用connection.close()方法关闭池化连接时会发生什么? - Chaitanya Gudala
2
@Krnsa:通常情况下,它会被释放回池中,然后由池子里的人在测试/回收连接方面进行隐蔽的担忧。 - BalusC
显示剩余6条评论

73

在使用完数据库/资源对象后最好关闭它们。最好在 finally 块中关闭连接、结果集和语句对象。

在 Java 7 之前,所有这些资源都需要使用 finally 块关闭。如果你正在使用 Java 7,则可以按照以下方式关闭资源。

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

    // Statements
}
catch(....){}

现在,constmtrs对象成为try块的一部分,Java会在使用后自动关闭这些资源。


如果我的语句是隐式的,即在“try”块内有“ResultSet rs = conn.createStatement().executeQuery(sql);”怎么办? - Antares42
1
你将无法在 finally {} 块中引用它们以进行闭包。如果抛出异常,ResultSet 的 close() 方法将永远不会被调用。 - Dan
如果我不关闭它们会发生什么? - Alex78191
如果你不关闭它们,就可能会发生内存泄漏。 - Yadu Krishnan

15

实际上,最好使用try-with-resources块,当您退出try块时,Java将自动关闭所有连接。

对于任何实现AutoClosable接口的对象,都应该使用此方法。

try (Connection connection = getDatabaseConnection(); Statement statement = connection.createStatement()) {
    String sqlToExecute = "SELECT * FROM persons";
    try (ResultSet resultSet = statement.execute(sqlToExecute)) {
        if (resultSet.next()) {
            System.out.println(resultSet.getString("name");
        }
    }
} catch (SQLException e) {
    System.out.println("Failed to select persons.");
}

调用 getDatabaseConnection 方法只是虚构的。请将其替换为获取 JDBC SQL 连接或从池中获取连接的方法调用。


那么在这种情况下,您不必手动关闭连接吗? - Colin D
1
正确。您不必显式关闭连接。当try代码块的结尾到达时,它将被关闭。 - Joe
这个需要哪个版本的Java才能运行? - Peter Mortensen
Java 7,如果我没记错的话。 - Luciano Brum

14

只关闭StatementConnection就足够了,不需要显式关闭ResultSet对象。

Java文档对于java.sql.ResultSet的说明如下:

当语句对象被关闭、重新执行或用于检索多个结果序列中的下一个结果时,ResultSet对象将由生成它的Statement对象自动关闭。


感谢BalusC的评论"我不会依赖这个。某些JDBC驱动程序可能存在问题。"


29
我不会依赖这个。一些JDBC驱动程序在这方面失败了。例如,Oracle会显示“超出最大打开游标数”等错误。请明确关闭所有已打开的资源,没有任何借口。 - BalusC
1
我宁愿不使用不符合规范的驱动程序。 - Enerccio
3
正如BalusC所指出的那样,明确关闭连接而不是硬编码特定提供程序的依赖是良好的防御性编程。 - michaelok

11
是的。您需要关闭结果集、语句和连接。如果连接来自池,则关闭它实际上会将其发送回池以供重用。
通常,您必须在finally{}块中执行此操作,以便如果抛出异常,您仍然有机会关闭它。
许多框架将为您处理资源分配/释放问题。例如Spring的JdbcTemplateApache DbUtils 具有方法来处理关闭结果集/语句/连接无论是否为空(并在关闭时捕获异常),这也可能有所帮助。

1
当我插入一个“finally”时,eclipse会用高亮方式告诉我它是错误的。这应该放在catch块之后吗? - onaclov2000
是的。try{}catch{}finally{}。顺便说一下,catch{}是可选的,就像finally{}一样。 - Brian Agnew
我把“close”语句移到了finally中,但它们只是说“sqlexception”,有什么建议吗? - onaclov2000
1
close() 抛出 SQLException 异常。你必须处理它。请参考 DbUtils.closeQuietly() 来静默处理此异常。 - Brian Agnew
通常你需要在finally{}块中完成这个操作:这是不是太晚了?关闭操作难道不应该直接完成吗? - Peter Mortensen
显示剩余2条评论

7

是的,你需要关闭 Connection。否则,数据库客户端通常会保持套接字连接和其他资源处于打开状态。


2
直到它退出。这会占用客户端和服务器端的各种有限资源。如果客户端经常这样做,可能会对客户端本身、数据库服务甚至运行在客户端或服务器机器上的其他应用程序造成问题。 - Stephen C

2

更好的方法是使用“Try With Resources”代码块

try (Connection connection = DriverManager.getConnection(connectionStr, username, password)) {
    try (PreparedStatement statement = connection.prepareStatement(query)) {
        statement.setFetchSize(100);
        try (ResultSet resultSet = statement.executeQuery()) {
            List<String> results = new ArrayList<>();
            while (resultSet.next()) {
                String value = resultSet.getString(1);
                results.add(value);
                System.out.println(value);
            }
            return results;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接