本文目录导读:

- 使用
try-with-resources(Java 7+)—— 最推荐 - 使用
finally块(传统、手动)—— 可靠但冗长 - 在
try-with-resources中嵌套结果集处理—— 仅限简单场景 - 使用连接池(最佳实践)—— 推荐
- 关键原则与常见误区
优雅地关闭数据库连接,核心在于确保资源被正确释放,同时避免在关闭过程中发生异常导致程序崩溃,通常有几种主流且优雅的方式,按推荐程度排序如下:
使用 try-with-resources(Java 7+)—— 最推荐
这是最优雅、最简洁的方式,它自动实现了 AutoCloseable 接口的关闭逻辑,无需显式写 close()。
// 注意:Connection、Statement、ResultSet 都实现了 AutoCloseable
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
// 处理结果集
while (rs.next()) {
// ...
}
} catch (SQLException e) {
// 处理异常(包括连接、查询或关闭时抛出的异常)
log.error("数据库操作失败", e);
}
// 自动关闭:无论正常结束还是抛出异常,rs -> stmt -> conn 会按创建顺序的逆序自动关闭
优雅之处:代码极简、零资源泄漏、异常处理集中。
使用 finally 块(传统、手动)—— 可靠但冗长
在 Java 7 之前或某些特殊场景下使用,需要手动在 finally 中关闭,并且要处理关闭时可能抛出的异常。
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(sql);
rs = stmt.executeQuery();
// 处理业务
} catch (SQLException e) {
log.error("查询失败", e);
} finally {
// 优雅关闭:顺序逆序,每个关闭都单独 try-catch,防止一个失败影响后续
closeQuietly(rs);
closeQuietly(stmt);
closeQuietly(conn);
}
// 辅助方法:忽略关闭时的异常(或仅记录日志)
private void closeQuietly(AutoCloseable resource) {
if (resource != null) {
try {
resource.close();
} catch (Exception e) {
// 通常记录日志即可,不建议转换为 RuntimeException 抛出
log.warn("关闭资源时发生异常", e);
}
}
}
优雅之处:通过辅助方法 closeQuietly 避免了 null 检查和异常处理重复,比直接写 if(conn!=null) conn.close()... 干净很多。
在 try-with-resources 中嵌套结果集处理—— 仅限简单场景
如果需要在连接关闭后继续使用 ResultSet(不推荐),可以手动控制:
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
// ResultSet 不放在 try 里,因为它依赖于 stmt
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
// ... 处理数据
}
}
}
使用连接池(最佳实践)—— 推荐
实际生产中,很少直接管理 Connection,而是通过连接池(如 HikariCP、Druid、Tomcat JDBC Pool),连接池的close()实际上是归还连接到池中,而非真正关闭。
// 使用连接池(以 HikariCP 为例)
DataSource dataSource = new HikariDataSource(config);
// 代码完全可以使用 try-with-resources
try (Connection conn = dataSource.getConnection()) {
// ... 业务逻辑
}
// conn.close() 执行后,连接被归还到池中,保持活跃,下次复用
优点:
- 避免频繁创建/销毁连接的开销。
- 池内连接可自动检测失效、重连。
- 关闭连接时几乎无异常(连接池内部处理了资源回收)。
关键原则与常见误区
| 原则 | 说明 |
|---|---|
| 逆序关闭 | 先关闭 ResultSet,再 Statement,Connection。try-with-resources 自动保证此顺序。 |
| 不要重复关闭 | 关闭 Connection 后,其下属的 Statement 可能也会自动关闭(取决于驱动实现,但不保证)。主动逆序关闭最安全。 |
| 避免在 finally 中关闭失败导致异常掩盖 | 在 finally 中关闭资源时若抛出异常,会覆盖 try 块中的原始异常(丢失错误),解决方案:要么用 try-with-resources,要么在 finally 中捕获关闭异常并记录日志,不要抛出。 |
| 不要手动设置 null | 如 conn = null; 对于资源释放毫无意义,GC 会处理对象回收,但关闭打开的资源是程序的责任。 |
- 最优雅:
try-with-resources(Java 7+)。 - 最健壮:结合连接池 +
try-with-resources。 - 特殊情况:如果必须手动关闭(如旧项目),使用
closeQuietly()辅助方法。
一句话最佳实践:
使用连接池,并在
try-with-resources块中获取和使用连接,不用再操心关闭细节。