JDBC中关于Connection, PreparedStatement, ResultSet是否关闭的一些思考

时间:2022-09-20 11:51:10

如果你不使用连接池,那么就没有什么问题,一旦Connection关闭,数据库物理连接就被释放,所有相关Java资源也可以被GC回收了。但是如果你使用连接池,那么请注意,Connection关闭并不是物理关闭,只是归还连接池,所以PreparedStatement和ResultSet都被持有,并且实际占用相关的数据库的游标资源,在这种情况下,只要长期运行,往往就会报“游标超出数据库允许的最大值”的错误,导致程序无法正常访问数据库。

在使用java开发后台应用程序的时候,如果需要使用数据库,特别是试用第三方的数据库连接池的时候,使用完PreparedStatement等一定要手动关闭,最好是将关闭的代码写到finally中,保证一定能够完成关闭。   

原因有如下两点:

1、第三方的数据库连接池,使用的时候,获取到Connection之后,使用完成,调用的关闭方法(close()) ,并没有将Connection关闭,只是放回到连接池中,如果调用的这个方法,而没有手动关闭PreparedStatement等,则这个PreparedStatement并没有关闭,这样会使得开发的程序内存急速增长,java的内存回收机制可能跟不上速度,最终造成Out of memory Error。

2、如过在PreparedStatement等调用的时候,发生异常,则这个PreparedStatement是没有被关闭的,因此最好将PreparedStatement等的关闭写到finally代码中。下面是本人

在本机上试过的一个测试实例

public static List<OrgzMetaDto> queryMetaByOrgzName1() {
String sql = "SELECT * FROM test_table limit 0, 10000";
List<OrgzMetaDto> orgzs = new ArrayList<OrgzMetaDto>();
try {
pst = conn.prepareStatement(sql);
ResultSet resultSet = pst.executeQuery();
while (resultSet.next()) {
OrgzMetaDto dto = new OrgzMetaDto();
dto.setUuid(resultSet.getString("uuid"));
orgzs.add(dto);
}
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
return orgzs;
}

public static void main(String[] args) throws Exception {
for (int i=0; i<1000; i++) {
System.out.println(i);
queryMetaByOrgzName1();
}
}

可以自行用类似于上述的代码测试一下 , 测试的时候关注本机物理内存的占用状况
在有resultSet.close()这句代码的时候,内存占用并不会发生太大变化,而一旦不关闭结果集,内存占用将会急速增长,甚至程序被hang住,我猜测可能就是上述所说的“游标超出数据库允许的最大值”,就算不阻塞,内存占用继续增长,将出现内存溢出问题