Java当中的ResultSet是什么、干什么用就不多说了,说说我前几天在这里栽的一个跟头。
先上一段测试代码看看问题:
public class TestResult { public static void main(String[] args){ String sql = "select id,username from s_user"; ResultSet rs = null; try{ rs = execQuery(sql); while(rs.next()){ System.out.println("序号:"+rs.getInt(1)+"\t用户名:"+rs.getString(2)); } }catch(Exception e){ e.printStackTrace(); }finally{ closeResultSet(rs); } } /** * 该方法用来执行查询sql * @param sql:要执行查询的sql * @return ResultSet:结果集合 * */ public static ResultSet execQuery(String sql){ Connection conn=null;//连接 PreparedStatement pst= null; ResultSet rs=null; try{ conn = com.wjl.C3P0Dao.getConn(); if(conn!=null){ pst = conn.prepareStatement(sql); rs = pst.executeQuery(); } }catch(Exception e){ e.printStackTrace(); }finally{ System.out.println("1111111111111111"); closeConn(rs,pst,conn); } System.out.println("22222222222222"); return rs; } /** * 资源关闭 * @param rs * @param stmt * @param conn */ public static void closeConn(ResultSet rs, Statement stmt, Connection conn) { if (rs != null) { try {rs.close(); } catch (SQLException e){e.printStackTrace();} } if (stmt != null) { try {stmt.close(); } catch (SQLException e){e.printStackTrace();} } if (conn != null) { try {conn.close(); } catch (SQLException e) {e.printStackTrace();} } } /** * 资源关闭 */ public static void closeResultSet(ResultSet rs) { if (rs != null) { try {rs.close(); } catch (SQLException e) {e.printStackTrace();} } } }
以上是一个再寻常不过的JDBC查询代码了,这个代码看似没有问题,但运行起来准报错:You can't operate on a closed ResultSet!!!。因为finally执行在return之前,return之前rs就已经关闭了,自然就无法操作一个已经关闭了的ResultSet了。那我不关闭ResultSet不就完了,于是注释掉closeConn()方法中关闭ResultSet那一段,再执行,依旧报错:You can't operate on a closed ResultSet!!!。这就奇怪了,我明明没有关闭为什么还说关闭了呢,看看网上的解释和处理意见:
1.垃圾回收机制可以自动关闭它们;
2.Statement关闭会导致ResultSet关闭;
3.Connection关闭不一定会导致Statement关闭。
V6使用的是数据库连接池,Connection关闭并不是物理关闭,只是归还连接池,所以Statement和ResultSet有可能被持有,并且实际占用相关的数据库的游标资源,在这种情况下,只要长期运行就有可能报“游标超出数据库允许的最大值”的错误,导致程序无法正常访问数据库。
解决建议:
(1)由于垃圾回收的线程级别是最低的,为了充分利用数据库资源,有必要显式关闭它们,尤其是使用Connection Pool的时候;
(2)最优经验是按照ResultSet,Statement,Connection的顺序执行close;
(3)为了避免由于java代码有问题导致内存泄露,需要在rs.close()和stmt.close()后面一定要加上rs = null和stmt = null;
(4) 如果一定要传递ResultSet,应该使用RowSet,RowSet可以不依赖于Connection和Statement。Java传递的是引用,所以如果传递ResultSet,你会不知道Statement和Connection何时关闭,不知道ResultSet何时有效。
( 引用链接:http://blog.csdn.net/hantiannan/article/details/7904855)
根据建议,使用RowSet来代替ResultSet返回数据。
Rowset的使用:http://blog.csdn.net/id19870510/article/details/5988438
RowSet和ResultSet的区别:http://blog.csdn.net/amaryh/article/details/5256090
根据RowSet各个接口的特性,推荐使用CachedRowSet,这个是不与statement和Connection关联的,即使前两个关闭了它已经能用。因此将上面的代码改成使用CachedRowSet来返回数据,代码如下:
public class TestResult2 { public static void main(String[] args){ String sql = "select id,username from s_user"; CachedRowSetImpl rs = null; try{ rs = execQuery(sql); while(rs.next()){ System.out.println("序号:"+rs.getInt(1)+"\t用户名:"+rs.getString(2)); } }catch(Exception e){ e.printStackTrace(); }finally{ closeCachedRowSet(rs); } } /** * 该方法用来执行查询sql * @param sql:要执行查询的sql * @return CachedRowSetImpl:结果集合 * */ public static CachedRowSetImpl execQuery(String sql){ Connection conn=null;//连接 PreparedStatement pst= null; ResultSet rs=null; CachedRowSetImpl rowset = null; try{ conn = com.wjl.C3P0Dao.getConn(); if(conn!=null){ pst = conn.prepareStatement(sql); rowset=new CachedRowSetImpl(); rowset.populate(pst.executeQuery()); } }catch(Exception e){ e.printStackTrace(); }finally{ closeConn(rs,pst,conn); } return rowset; } /** * 资源关闭 * @param rs * @param stmt * @param conn */ public static void closeConn(ResultSet rs, Statement stmt, Connection conn) { ...... } /** * 资源关闭 * @param CachedRowSetImpl rs */ public static void closeCachedRowSet(CachedRowSetImpl rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
另外,也可以使用Result来代替ResultSet返回数据,具体使用方法可以参考如下链接:
http://www.cnblogs.com/zzlp/p/5120176.html
祝大家好运!