1、最近做了一个项目,扫描读取了第三方数据库的数据,结果本来在公司测试没有问题的程序在客户那边一直报如下错误:
java.sql.SQLException: 关闭的连接: next
代码如下:
//第三方His数据库连接 Connection connOrc = null; pst_zfy = connOrc.prepareStatement(sql); pst_drxf.setTimestamp(1, start); pst_drxf.setTimestamp(2, end); rs_drxf = pst_drxf.executeQuery(); while(rs_drxf.next()){ zyh = rs_drxf.getString(1); drxf = rs_drxf.getDouble(2); } }catch(Exception e){ e.printStackTrace(); }finally{ DBConnector.close(rs_drxf, pst_drxf, connOrc);//该close()方法如下: }
/** * 关闭数据库连接,释放数据库连接 * * @param rs * @param pstmt * @param con */ public static void close(ResultSet rs, PreparedStatement pstmt, Connection con) { if(null != rs) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (null != pstmt) { try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (null != con) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } }
一般来说,造成该错误的原因只有一个就是在调用ResultSet的next()方法时对应的Connection、PreparedStatement或者ResultSet已经关闭(调用了其close()方法)。通常来说关闭PreparedStatement和ResultSet对象,肯定是我们写的程序自己主动关闭了,至于这个错误,我们可以检查自己写的程序代码;第二个就是关闭Connection,对于这个对象的关闭,原因比较多:可能是我们自己关闭、物理网络原因造成数据库连接断开或者外部程序统一控制数据库连接出现问题等等。
分析原因:
①、提前主动关闭Connection、PreparedStatement或者ResultSet,应该不是该原因,这个错误一般大家都不会犯,即使犯了,在第一次调试后第一时间就会找出来,jdbc查询数据库数据标准流程都是:创建连接-->>执行查询-->>遍历数据-->>关闭连接、释放资源。遵循该流程应该都不会出现该低级错误。既然不是该错误,那应该与我们写的程序关系不到,应该是什么其他原因造成了数据库连接关闭,现在来找关闭Connection的其他原因。
②、检查是否是其他物理原因造成的数据库连接Connectiion关闭了,通过日志记录,打印Connection连接对象,发现数据库连接仍然能正常创建成功。说道这里,根据上面的程序,不知作为看客的您们是否发现问题所在了?
③、刚才说了,能关闭数据库连接Connection的还有统一管理数据库连接的外部插件-------数据库连接池!!这里我就用了proxool数据库连接池,我的配置如下:
<proxool> <alias>orc</alias> <driver-url> jdbc:oracle:thin:@127.0.0.1:1521:his_db </driver-url> <driver-class>oracle.jdbc.driver.OracleDriver</driver-class> <driver-properties> <property name="user" value="sa" /> <property name="password" value="123456" /> </driver-properties> <minimum-connection-count>1</minimum-connection-count> <maximum-connection-count>20</maximum-connection-count> <house-keeping-sleep-time>30</house-keeping-sleep-time> <house-keeping-test-sql>select 0</house-keeping-test-sql> <trace>false</trace> <statistics-log-level>ERROR</statistics-log-level> </proxool>
proxool是当前非常优秀的数据库连接池工具,相信许多java开发者应该都用过它,通常配置该连接池时,许多选项都可以采用默认,无需配置,但是在特殊情况下还是要合理配置的。proxool里面能关闭数据库连接的配置有两个分别如下:
<!-- 一个活动连接的最长时间,默认5分钟,单位毫秒 --> <maximum-active-time>300000</maximum-active-time> <!-- 一个连接的最长活动时间,默认4小时,单位毫秒 --> <maximum-connection-lifetime>14400000</maximum-connection-lifetime>
最长活动时间为4小时,应该不成问题,那么活动连接最长时间默认5分钟,出问题的可能性就非常大了,这个5分钟要求我们在创建数据库连接到查询完数据并关闭必须在5分钟之内完成,否则自动关闭数据库连接。经过仔细分析所查询数据库的数据,发现数据库对应表中的数据高达上亿,从这么庞大的数据中搜索查询一次数据至少需要4至5分钟,然后再遍历ResultSet进行处理,结果所耗时间远远超过了5分钟,所以出现上面的java.sql.SQLException: 关闭的连接: next这个错误也就理所当然了。
现将该配置改为15分钟(可根据自己项目的需要进行配置,建议大家都改下,5分钟一般来说都不够,除非你的项目非常小,数据量非常少而且查询数据后的处理业务逻辑非常简单)。更改后的配置如下。
<proxool>
<alias>orc</alias>
<driver-url>
jdbc:oracle:thin:@192.168.1.12:1521:bsrun
</driver-url>
<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
<driver-properties>
<property name="user" value="bsquery" />
<property name="password" value="admin" />
</driver-properties>
<minimum-connection-count>1</minimum-connection-count>
<maximum-connection-count>20</maximum-connection-count>
<house-keeping-sleep-time>30</house-keeping-sleep-time>
<house-keeping-test-sql>select 0</house-keeping-test-sql>
<maximum-active-time>900000</maximum-active-time>
<trace>false</trace>
<statistics-log-level>ERROR</statistics-log-level>
</proxool>
有关proxool的配置,及其各种属性的作用、默认值参见我另一篇文章:proxool配置详解。