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
祝大家好运!