Java - 连接关闭后无法使用ResultSet

10

我在关闭MySQL连接时遇到了问题。

我收到了以下错误:

java.sql.SQLException: ResultSet关闭后不允许操作

我的代码:

public static ResultSet sqlquery (String query)
{
 ResultSet rs=null;
 Connection connection=null;
 Statement st=null;
 try{   
     Class.forName("com.mysql.jdbc.Driver");
     connection = DriverManager.getConnection("databaseadress","username","password");
     st = connection.createStatement();  
     rs = st.executeQuery(query);

    }catch(SQLException e){System.out.println("SQL error: " + e);}
      catch(Exception e){System.out.println("Error: " + e);}
       finally {
       try{
          if(rs != null) rs.close();
          if(st!= null) st.close();
          if(connection != null)  connection.close();
  }catch(SQLException e){System.out.println("SQL error : " + e);}

    }
     return rs;
}

1
我不是数据库专家,但我觉得在你完成ResultSet之前不要关闭连接。那么当你改变代码以反映这一点时会发生什么呢?为什么不在方法内提取ResultSet中的数据,并返回一个ArrayList,其中包含你希望用数据创建的任何对象,然后从该方法返回此列表呢? - Hovercraft Full Of Eels
1
你已经关闭了ResultSet,所以所有的数据都被删除了。为什么你还想要使用它呢?在关闭之前提取数据并返回数据。 - Marco Acierno
代码的哪一行出现了异常?你确定它是你在这里展示的代码之一吗? - Dawood ibn Kareem
看起来他正在返回ResultSet对象,并在此处未显示的代码中稍后获取异常。 - Jim Garrison
@JimGarrison 是的,我知道。我希望通过检查堆栈跟踪,他能够找出来。 - Dawood ibn Kareem
是的,我在代码的其他部分使用了sqlquerry函数。它在连接未关闭的情况下工作正常,但我意识到这不是一个好主意 ;) - mlodikkal
4个回答

14
JDBC不会在ResultSet中返回所有查询结果,因为可能有太多的结果需要获取。相反,它会给你一些可以用来检索结果的东西,但是连接关闭时这些内容就会消失。因此,在关闭数据库连接后从方法中传递它回来时,没有其他内容可以使用它。
相反的做法是让这个方法使用resultSet来填充一个对象或对象集合,并将填充了数据的对象返回。
如果您更改代码以传递rowMapper(它接受resultSet并返回填充了当前行的对象),并使用它来填充一个容器对象并传递回去,那么您将拥有与您编写的内容一样可重复使用的内容,但它实际上可以正常工作,因为它不依赖于调用完成后保持连接打开状态。
以下是重新编写以使用rowmapper的示例代码,除去了一些不必要的异常捕获,并修复了一个错误,该错误会导致在某些情况下无法关闭连接:
public static List<T> sqlquery (String query, RowMapper<T> rowMapper) throws SQLException
{
    Connection connection=null;
    Statement st=null;
    ResultSet rs=null;
    // don't need Class.forName anymore with type4 driver     
    connection = DriverManager.getConnection("databaseadress","username","password");
    st = connection.createStatement();  
    rs = st.executeQuery(query);
    List<T> list = new ArrayList<T>();
    while (rs.next()) {
        list.add(rowMapper.mapRow(rs));
    }
    // don't let exception thrown on close of
    // statement or resultset prevent the
    // connection from getting closed
    if(rs != null) 
        try {rs.close()} catch (SQLException e){log.info(e);}
    if(st!= null) 
        try {st.close()} catch (SQLException e){log.info(e);}
    if(connection != null)  
        try {connection.close()} catch (SQLException e){log.info(e);}
    return list;
}

如果您不像上面所示的那样单独捕获每个关闭时抛出的异常,那么如果语句或结果集在关闭时引发异常,则有可能无法关闭连接。
这类似于spring-jdbc的做法,它定义了一个RowMapper
public interface RowMapper<T> {
    T mapRow(ResultSet, int rowNum) throws SQLException;
}

下一步将是参数化您的查询,这样您就不必用引号括起参数值或担心SQL注入。请参阅此答案以了解spring-jdbc如何处理此问题的示例。长期来看,最好采用spring-jdbc或类似的东西,而不是分散地重新发明它。

谢谢解释如何处理 ResultSet。 - mlodikkal

11
这是JDBC的工作方式。在您的代码中,您关闭了ResultSetConnection,此后ResultSet不再可用。如果要让它可用,必须保持其(以及Connection)打开。
但是,如果您返回ResultSet,则应重构代码,使调用方法提供Connection

谢谢,这可能是最好的方式 :) - mlodikkal

3
RowSetFactory factory = RowSetProvider.newFactory();
CachedRowSet rowset = factory.createCachedRowSet();
rowset.populate(ResultSet data)

/*现在你可以关闭连接并准备语句*/


1
一旦连接关闭,您就无法再使用任何资源(语句、预处理语句、结果集),它们都会自动关闭。因此,在资源打开时进行所有处理。
尝试填充并返回DTO,这样您就可以在不保持连接的情况下获取所需数据。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接