【问题标题】:Java - Can't use ResultSet after connection closeJava - 连接关闭后无法使用 ResultSet
【发布时间】:2014-10-19 01:42:57
【问题描述】:

我在关闭与 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;
}

【问题讨论】:

  • 我不是数据库专家,但在我看来,在完成 ResultSet 之前,您不想关闭连接。那么当您更改代码以反映这一点时会发生什么?为什么不在方法中从 ResultSet 中提取数据,并返回您希望使用数据创建的任何对象的 ArrayList,然后从方法中返回此列表?
  • 您关闭了 ResultSet,所以所有内容都被删除了。为什么要使用它?在关闭之前提取数据并返回数据
  • 异常发生在哪一行代码?你确定这是你在这里展示的吗?
  • 看来他正在返回ResultSet 对象,并且稍后在此处未显示的代码中获取异常。
  • @JimGarrison 是的,我知道。我希望通过检查堆栈跟踪,他能弄明白。

标签: java sql jdbc


【解决方案1】:

这就是 JDBC 的工作方式。在您的代码中,您关闭了ResultSetConnection,之后ResultSet 将不再可用。如果你想让它可用,你必须让它(和Connection)保持打开状态。

但是,如果您返回ResultSet,您应该重构您的代码,以便调用方法提供Connection

【讨论】:

  • 谢谢,这可能是最好的方法:)
【解决方案2】:

JDBC 不会在 ResultSet 中返回查询的所有结果,因为它们可能太多,无法急切地获取它们。相反,它为您提供了一些可以用来检索结果的东西,但是当连接关闭时它就会消失。因此,当您在关闭数据库连接后将其从您的方法中传回时,其他任何东西都无法使用它。

您可以做的是让此方法使用 resultSet 填充对象或对象集合,并将填充的对象传回。

如果您将代码更改为传入 rowMapper(它接受一个 resultSet 并传回一个填充了结果集中当前行的对象),并使用它来填充您传回的容器对象,那么您将具有与您编写的内容一样可重用的内容,但实际上有效,因为它不依赖于在调用完成后保持连接保持打开状态。

这是您重写的示例代码以使用行映射器,摆脱一些不必要的异常捕获,并修复在某些情况下会阻止连接关闭的错误:

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 如何处理此问题的示例,请参阅this answer。这里的长期答案是,最好采用 spring-jdbc 或类似的东西,而不是零碎地重新发明它。

【讨论】:

  • 感谢您解释如何处理 ResultSet
【解决方案3】:

一旦连接关闭,您将无法再使用任何资源(语句、准备好的语句、结果集),所有资源都会自动关闭。因此,您需要在资源打开时进行所有处理。

尝试填充并返回 DTO,这样您就可以获得所需的数据,而无需保持连接处于活动状态。

【讨论】:

    【解决方案4】:
    RowSetFactory factory = RowSetProvider.newFactory();
    CachedRowSet rowset = factory.createCachedRowSet();
    rowset.populate(ResultSet data)
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-09
      • 2018-04-03
      • 2017-09-03
      • 1970-01-01
      • 2020-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多