在 JDBC 编程中,
Connection(数据库连接)、PreparedStatement(预编译 SQL 语句)、ResultSet(查询结果集)这三个对象必须全部关闭,核心原因是它们都占用了稀缺的系统资源(数据库服务器资源、本地内存资源、网络连接资源等),若不主动关闭,会导致「资源泄漏」,最终引发系统性能下降、服务不可用等严重问题。
下面分别拆解每个对象必须关闭的原因,以及关闭顺序的逻辑:
一、为什么每个对象都要关闭?
1.
ResultSet— 释放查询结果占用的内存和游标
- 本质:
ResultSet是数据库查询结果的「内存映射 / 游标引用」,它不仅占用 Java 程序的本地内存(存储结果数据),还占用数据库服务器的「游标资源」(数据库用游标跟踪当前读取到结果集的哪个位置)。- 不关闭的危害:
- 本地内存泄漏:大量未关闭的
ResultSet会堆积在 JVM 内存中,导致内存溢出(OOM);- 数据库游标耗尽:数据库服务器的游标数量是有限的(比如 MySQL 默认游标数有上限),若游标被耗尽,后续所有查询都会失败(报错 “游标超出限制”);
- 结果集锁定:部分数据库(如 Oracle)的
ResultSet会持有表的行锁,不关闭会导致其他事务无法修改数据,引发并发问题。
2. PreparedStatement — 释放数据库的 SQL 执行资源
- 本质:
PreparedStatement是预编译后的 SQL 语句对象,它在数据库服务器端会占用「执行计划缓存」「会话资源」(比如 MySQL 的statement句柄),即使 SQL 执行完毕,这些资源也不会自动释放。- 不关闭的危害:
- 数据库资源耗尽:每个
PreparedStatement对应数据库的一个执行句柄,句柄数量有限,耗尽后无法执行任何新的 SQL;- 预编译缓存污染:未关闭的
PreparedStatement会一直占用预编译缓存,导致真正需要缓存的 SQL 无法被缓存,降低查询性能;- 内存泄漏:
PreparedStatement会持有Connection的引用,若它不被关闭,可能导致Connection无法被 JVM 垃圾回收(GC),间接造成连接泄漏。
3. Connection — 释放最稀缺的数据库连接资源
- 本质:
Connection是 Java 程序与数据库服务器之间的「网络连接 + 数据库会话」,是 JDBC 中最宝贵的资源(数据库服务器的最大连接数有限,比如 MySQL 默认最大连接数是 151)。- 不关闭的危害:
- 连接池耗尽:实际开发中都会用「数据库连接池」(如 Druid、HikariCP),连接池中的连接数是固定的(比如 10-50 个),若
Connection不关闭,连接池会被耗尽,后续所有需要数据库操作的请求都会阻塞,系统瘫痪;- 数据库性能下降:每个空闲的
Connection都会占用数据库的会话资源、内存,大量空闲连接会导致数据库服务器负载飙升,响应变慢;- 网络资源浪费:空闲连接会维持 TCP 连接,占用网络端口和带宽。
二、为什么关闭顺序是 rs → pstmt → conn?
// 关闭资源
public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
System.err.println("资源关闭失败!");
}
}
关闭顺序是
ResultSet → PreparedStatement → Connection,这个顺序不能颠倒,原因是:
- 上层对象依赖下层对象的资源:
ResultSet依赖PreparedStatement的执行上下文,PreparedStatement依赖Connection的数据库连接;- 若先关闭底层对象(如先关
conn),再关闭上层对象(如rs或pstmt),会导致上层对象的关闭操作失败(因为依赖的底层资源已释放),最终造成上层对象的资源泄漏。
举个反例:如果先关
conn,再关rs,此时rs对应的数据库游标已随conn关闭而失效,调用rs.close()会抛出SQLException,导致rs占用的本地内存无法释放。
三、补充:为什么不能依赖 “自动关闭”?
有人可能会觉得:“JVM 的垃圾回收(GC)会不会自动回收这些对象,释放资源?”—— 答案是 不会,因为:
- 这些对象的「核心资源」(数据库连接、游标、执行句柄)是在数据库服务器端或操作系统层面,GC 只能回收 Java 堆内存中的对象本身,无法操作底层系统资源;
- 即使部分数据库驱动实现了 “关闭
Connection时自动关闭关联的PreparedStatement和ResultSet”,但这是驱动的 “额外行为”,不是 JDBC 标准,不同驱动(如 MySQL、Oracle 驱动)的实现不一致,不能依赖;- 主动关闭是 “及时释放资源”,而等待
Connection关闭后再释放上层对象,会导致资源占用时间变长,在高并发场景下依然可能引发资源耗尽。
总结
- 三个对象都要关闭:因为它们分别占用数据库游标、SQL 执行句柄、数据库连接等稀缺资源,不关闭会导致资源泄漏;
- 关闭顺序不能乱:从上层到下层(
rs → pstmt → conn),避免因底层资源提前释放导致上层对象关闭失败。
在 Java 7+ 中,推荐使用「try-with-resources」语法(自动关闭实现了
AutoCloseable接口的对象),简化关闭代码,且能保证资源一定被关闭:
// 自动关闭 conn、pstmt、rs(无需手动写 close 方法)
try (Connection conn = DriverManager.getConnection(url, user, pwd);
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
// 业务逻辑:处理 rs
} catch (SQLException e) {
System.err.println("数据库操作失败!");
}
这种语法的关闭顺序和手动关闭一致(rs → pstmt → conn),是更安全、简洁的最佳实践。
203

被折叠的 条评论
为什么被折叠?



