20

当我ResultSet关闭Connection. 但是我想返回ResultSet并在另一种方法中使用它,然后我不知道在哪里关闭Connectionand PreparedStatement

public ResultSet executeQuery(String sql, String[] getValue)
{
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try
    {
        conn = getConn();
        pstmt = conn.prepareStatement(sql);
        if (getValue != null)
        {
            for (int i = 0; i < getValue.length; i++)
            {
                pstmt.setString(i + 1, getValue[i]);
            }
        }
        rs = pstmt.executeQuery();
    } catch (Exception e)
    {
        e.printStackTrace();
        closeAll(conn, pstmt, rs);
    }
    return rs;
}

我已经closeAll(conn, pstmt, null);进入了 catch 块,因为我发现如果我把它放在 finally 块中,我会rs在它返回之前立即丢失我的。现在当我想关闭 时rs,我无法关闭connand pstmt。有什么解决办法吗?

4

10 回答 10

33

用于CachedRowSet在断开连接后保留信息

Connection con = ...
ResultSet rs = ...

CachedRowSet rowset = new CachedRowSetImpl();
rowset.populate(rs);

con.close()
于 2009-12-15T20:14:53.380 回答
20

对此进行编码的一种简洁方法是传入一个对象,该对象具有一个采用结果集的回调方法。

您的另一个方法使用带有其结果集处理代码的回调方法创建对象,并将其传递给执行 SQL 的方法。

这样一来,您的 SQL 和 DB 代码将保留在它所属的位置,您的结果集处理逻辑更接近您使用数据的位置,并且您的 SQL 代码在应该清理的时候进行清理。

  interface ResultSetCallBack{
    void handleResultSet(ResultSet r);
  }

  void executeQuery(..., ResultSetCallBack cb){
    //get resultSet r ...
    cb.handleResultSet(r);
    //close connection
  }

  void printReport(){
    executeQuery(..., new ResultSetCallBack(){
      public void handleResultSet(ResultSet r) {
        //do stuff with r here
      }
    });
  }
于 2009-12-15T20:14:28.327 回答
5

永远不应该将ResultSet(或StatementConnection)传递给方法块之外的公共,在这些方法块之外,它们将被获取关闭以避免资源泄漏。一种常见的做法是将 映射ResultSet到一个List<Data>whereData只是一个表示感兴趣数据的 javabean 对象。

这是一个基本示例:

public class Data {
    private Long id;
    private String name;
    private Integer value;
    // Add/generate public getters + setters.
}

这是如何正确处理它的基本示例:

public List<Data> list() throws SQLException {
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    List<Data> list = new ArrayList<Data>();

    try {
        connection = database.getConnection();
        statement = connection.prepareStatement("SELECT id, name, value FROM data");
        resultSet = statement.executeQuery();
        while (resultSet.next()) {
            Data data = new Data();
            data.setId(resultSet.getLong("id"));
            data.setName(resultSet.getString("name"));
            data.setValue(resultSet.getInt("value"));
            list.add(data);
        }
    } finally {
        if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
        if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
        if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
    }

    return list;
}

您可以按如下方式使用它:

List<Data> list = dataDAO.list();

要了解有关 JDBC 最佳实践的更多信息,您可能会发现这篇基本的启动文章也很有用。

于 2009-12-15T20:38:05.753 回答
3

按照您现在的方式,连接永远不会关闭,这将在以后(如果不是立即)为您的程序和 RDBMS 造成问题。最好创建一个 Java 类来保存 ResultSet 中的字段并将其返回。ResultSet 链接到连接,因此无法返回它并关闭连接。

于 2009-12-15T20:13:40.157 回答
3

ResultSet关闭Connection和/或PreparedStatement. 因此,您需要将一个对象传递给此方法,以在该对象上进行回调。

所有的清理工作都应该分finally块完成。

改写成这样

public ResultSet executeQuery(
    String sql,
    String[] getValue,
    CallbackObj cbObj
  ) throws SQLException
{
  final Connection conn = getConn( );

  try
  {
    final PreparedStatement pstmt = conn.prepareStatement(sql);

    try
    {
      if (getValue != null)
      {
        for (int i = 0; i < getValue.length; i++)
        {
          pstmt.setString(i + 1, getValue[i]);
        }
      }

      final ResultSet rs = pstmt.executeQuery();

      try
      {
        cbObj.processResultSet( rs );
      }
      finally
      {
        // You may want to handle SQLException
        // declared by close
        rs.close( );
      }
    }
    finally
    {
      // You may want to handle SQLException
      // declared by close
      pstmt.close( );
    }
  }
  finally
  {
    // You may want to handle SQLException
    // declared by close
    conn.close( );
  }
}
于 2009-12-15T20:22:09.547 回答
3

您可以调用ResultSet.getStatement以检索Statement,并Statement.getConnection检索Connection.

从这些你可以编写一个closeResultSet实用方法,它将为你关闭所有 3 个,除了ResultSet.

于 2009-12-15T21:34:40.293 回答
3

我想返回 ResultSet 时在哪里关闭 JDBC 连接

实际上,您自己几乎已经回答了这个问题。正如您所试验的那样,关闭Connection将释放与其关联的 JDBC 资源(至少,事情应该是这样工作的)。所以,如果你想返回一个ResultSet(我稍后会回来),你需要“稍后”关闭连接。一种方法显然是将连接传递给您的方法,如下所示:

public ResultSet executeQuery(Connection conn, String sql, String[] getValue);

问题是我真的不知道你的最终目标是什么以及为什么你需要这么低级的东西所以我不确定这是一个好建议。除非您正在编写低级 JDBC 框架(请不要告诉我您没有这样做),否则我实际上不建议返回ResultSet. 例如,如果您想提供一些业务类,请返回一些独立于 JDBC 的对象或它们的集合,正如其他人建议的那样,而不是ResultSet. 还要记住 aa所以如果你不应该使用 a那么你不应该使用 a 。RowSet ResultSetResultSetRowSet

就个人而言,我认为您应该使用一些辅助类而不是重新发明轮子。虽然 Spring 可能有点矫枉过正并且有一点学习曲线(如果你根本不知道的话就太多了),但 Spring 并不是唯一的方法,我强烈建议看看Commons DbUtils。更具体地说,看看QueryRunner特别是这个query()方法:

public <T> T query(String sql,
                   ResultSetHandler<T> rsh,
                   Object... params)
        throws SQLException

如您所见,此方法允许传递一个ResultSetHandler公开回调方法以转换ResultSets为其他对象的方法,如z5h 的答案中所述,并且 DbUtils 提供了多种实现,只需选择适合您需求的实现即可。还可以查看DbUtils该类的实用方法,例如DbUnit.close()您可能会发现方便关闭 JDBC 资源的各种实用方法。

真的,除非你有很好的理由这样做(我很想知道它们),不要再编写另一个 JDBC 框架,使用现有的解决方案,它会为你节省一些痛苦,更重要的是,一些错误您将受益于经过验证的优秀设计。正如我们所见,即使对于低级别的东西,也有现有的(和简单的)解决方案。至少,检查一下。

于 2009-12-16T16:42:27.090 回答
3

更简洁的方法是使用CachedRowSetImpl。但在 MySQL 5.x+ 上,按名称或标签选择列存在一些错误。

要与 MySQL 一起使用,请使用此版本: https ://stackoverflow.com/a/17399059/1978096

于 2013-07-01T07:10:42.487 回答
1

您真的不应该在较低级别处理 JDBC。请改用spring之类的框架,它会为您处理所有必需close()的操作。

于 2009-12-15T20:15:53.673 回答
1

我建议你做更多这样的事情:

public List<Map> executeQuery(Connection connection, String sql) throws SQLException
{
    List<Map> rows = new ArrayList<Map>();

    PreparedStatement stmt = null;
    ResultSet rs = null;

    try
    {
        pstmt = conn.prepareStatement(sql);
        rs = stmt.execute();
        int numColumns = rs.getMetaData().getColumnCount();

        while (rs.next())
        {
            Map<String, Object> row = new LinkedHashMap<String, Object>();
            for (int i = 0; i < numColumns; ++i)
            {
                String column = rs.getColumnName(i+1);
                Object value = rs.getObject(i+1);
                row.put(column, value);
            }
            rows.add(row);
        }
    } 
    finally
    {
        close(rs);
        close(stmt);
    }

    return rows;
}

public static void close(Statement s)
{
    try
    {
        if (s != null)
        {
            s.close();
        }
    }
    catch (SQLException e)
    {
        e.printStackTrace();
    }
}

public static void close(ResultSet rs)
{
    try
    {
        if (rs != null)
        {
            rs.close();
        }
    }
    catch (SQLException e)
    {
        e.printStackTrace();
    }
}
于 2009-12-15T23:26:52.383 回答