【问题标题】:Closing database connections in Java在 Java 中关闭数据库连接
【发布时间】:2011-01-14 13:50:35
【问题描述】:

我有点糊涂了。我从 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 一个,还是两者兼而有之?

该站点间歇性地关闭,但服务器一直说这是数据库连接问题。我怀疑它没有被关闭,但我不知道要关闭哪个(如果有的话)。

【问题讨论】:

  • 自行关闭连接始终是最佳实践,无需依赖其他驱动程序和模板来处理关闭。关闭连接失败将导致套接字和资源永远打开,直到崩溃(没有更多资源场景)或重新启动。

标签: java database-connection


【解决方案1】:

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

实际上,Java 中的安全模式是在完成操作后将 ResultSetStatementConnection(按此顺序)关闭在 finally 块中。像这样的:

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 */ }
}

但是,这仍然非常冗长,因此您通常最终使用辅助类来关闭 null 安全辅助方法中的对象,finally 块变成这样:

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

实际上,Apache Commons DbUtils 有一个 DbUtils 类正是这样做的,所以没有任何必要编写自己的。

【讨论】:

  • 很棒的帮助,谢谢!我没有注意到或考虑 conn != null 语句。
  • @Pascal Thivent:实际上我们不需要关闭所有这些。 《Core Java Volume 2 - Advanced Features》一书中写道:Statement 对象的close 方法会自动关闭关联的ResultSet,如果该语句具有打开的结果集。同样,Connection 类的close 方法关闭了Connection 的所有Statements
  • @Majid:除非它是一个池连接。然后这些语句就会泄露出去。
  • @BalusC: 你能解释一下当使用connection.close()方法关闭池连接时会发生什么
  • @Krnsa:通常,它会被释放回池中,而池中又会担心测试/获取连接。
【解决方案2】:

使用后关闭数据库/资源​​对象总是更好。 最好关闭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 会在使用后自动关闭这些资源。

【讨论】:

  • 如果我的陈述是隐含的,即ResultSet rs = conn.createStatement().executeQuery(sql);try 块内怎么办?
  • 您将无法在 finally {} 块中引用它们以进行关闭。如果抛出异常,则永远不会调用ResultSet的close()方法
  • 如果我不关闭它们会怎样?
  • 如果不关闭它们,可能会发生内存泄漏。
【解决方案3】:

只关闭StatementConnection 就足够了。无需显式关闭ResultSet 对象。

Java 文档中提到了java.sql.ResultSet

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


感谢BalusC for comments“我不会依赖它。一些 JDBC 驱动程序会失败。”

【讨论】:

  • 我不会依赖它。一些 JDBC 驱动程序在这方面失败了。例如。 Oracle 出现“超出最大打开游标”等问题。只需明确关闭所有打开的资源,没有任何借口。
  • 我宁愿不要使用不符合规范的驱动程序
  • 正如 BalusC 指出的那样,显式关闭连接而不是硬连线对特定提供程序的依赖是一种很好的防御性编程。
【解决方案4】:

是的。您需要关闭结果集、语句和连接。如果连接来自池,关闭它实际上会将其发送回池以供重用。

您通常必须在 finally{} 块中执行此操作,这样如果抛出异常,您仍然有机会关闭它。

许多框架会为您处理此资源分配/解除分配问题。例如春天的JdbcTemplateApache DbUtils 有方法来处理关闭结果集/语句/连接是否为空(并在关闭时捕获异常),这也可能有所帮助。

【讨论】:

  • 当我插入“终于”时,日食喜欢突出显示它,告诉我这是错误的。这应该在 catch 块之后进行吗?
  • 是的。尝试{}抓住{}最后{}。 catch{} 是可选的,顺便说一句。就像 finally{}
  • 我将“close”语句移到 finally,但他们只是说“sqlexception”,有什么建议吗?
  • close() 抛出 SQLException。你必须处理它。请参阅 DbUtils.closeQuietly() 以静默处理。
  • > 如果 conn.close() 没有发生,到底发生了什么?
【解决方案5】:

实际上,最好使用try-with-resources block,当您退出 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 连接或来自池的连接的调用。

【讨论】:

  • 那么在这种情况下您不必手动关闭连接吗?
  • 正确。您不必显式关闭连接。当到达 try 代码块的末尾时,它将被关闭。
  • 此功能的 Java 版本要求是什么?
  • Java 7,如果我没记错的话。
【解决方案6】:

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

【讨论】:

  • ...直到它退出。这限制了客户端和服务器端的各种有限资源。如果客户端做这种事情太多,它可能会导致客户端本身、数据库服务,甚至可能导致客户端或服务器机器上运行的其他应用程序出现问题。
【解决方案7】:

最好使用 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;
        }
    }
}

【讨论】:

    猜你喜欢
    • 2012-07-12
    • 2020-09-16
    • 1970-01-01
    • 2014-07-01
    • 1970-01-01
    • 2012-01-06
    • 1970-01-01
    • 2016-12-31
    相关资源
    最近更新 更多