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

发布于 2024-08-15 03:40:47 字数 974 浏览 8 评论 0原文

当我关闭Connection时,ResultSet似乎会自动关闭。 但我想返回ResultSet并在另一个方法中使用它,然后我不知道在哪里关闭ConnectionPreparedStatement

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时,我无法关闭connpstmt。有什么解决办法吗?

It seems that the ResultSet will be automatically closed when I close the Connection.
But I want to return the ResultSet and use it in another method, then I don't know where to close Connection and 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;
}

I've moved closeAll(conn, pstmt, null); into catch block because I found that if I put it in finally block I'll lost my rs immediately just before it returns.
Now when I want to close the rs, I can't close the conn and pstmt. Is there any solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

听风念你 2024-08-22 03:40:47

使用 CachedRowSet 在断开连接后保存信息

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

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

con.close()

Use CachedRowSet for holding info after disconnecting

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

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

con.close()
似最初 2024-08-22 03:40:47

一种干净的编码方法是传入一个对象,该对象具有接受结果集的回调方法。

您的其他方法使用回调方法及其 resultSet 处理代码创建对象,并将其传递给执行 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
      }
    });
  }

One clean way of coding this is to pass in an object that has a callback method that takes a result set.

Your other method creates the object with the callback method with it's resultSet handling code, and passes that to the method that executes the SQL.

That way, your SQL & DB code stays where it belongs, your result set handling logic is closer to where you use the data, and your SQL code cleans up when it should.

  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
      }
    });
  }
做个少女永远怀春 2024-08-22 03:40:47

您不应该永远ResultSet (或 StatementConnection)传递到它们所在的方法块之外的 public 中被收购并关闭以避免资源泄漏。常见的做法是将 ResultSet 映射到 List,其中 Data 只是表示感兴趣数据的 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 最佳实践的更多信息,您可以找到 这篇基本的启动文章也很有用。

You should never pass ResultSet (or Statement or Connection) into the public outside the method block where they are to be acquired and closed to avoid resource leaks. A common practice is just to map the ResultSet to a List<Data> where Data is just a javabean object representing the data of interest.

Here's a basic example:

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

and here's a basic example of how to handle it correctly:

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;
}

you can use it as follows:

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

To learn more about the best practices with JDBC you may find this basic kickoff article useful as well.

颜漓半夏 2024-08-22 03:40:47

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

The way you have it right now, the connection would never close which would cause problems later (if not immediately) for your program and the RDBMS. It would be better to create a Java class to hold the fields from the ResultSet and return that. The ResultSet is linked to the connection, so returning it and closing the connection is not possible.

何必那么矫情 2024-08-22 03:40:47

关闭 Connection 和/或 PreparedStatement 后,您将无法使用 ResultSet
因此,您需要将一个要对其进行回调的对象传递到此方法中。

所有清理工作都应该在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( );
  }
}

You can't use ResultSet after you've closed Connection and/or PreparedStatement.
So, you need to pass an object on which to make a callback into this method.

All cleanup should be done in finally blocks.

Rewrite it like this

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( );
  }
}
以歌曲疗慰 2024-08-22 03:40:47

当我想返回结果集时在哪里关闭 JDBC 连接

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

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

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

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

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

正如你所看到的,这个方法允许传递 ResultSetHandler 公开了一个回调方法,用于将 ResultSets 转换为其他对象,如 z5h 的答案 和 DbUtils 提供了几种实现,只需选择适合您需要的一种即可。另请查看 DbUtils 类,例如您可能会发现可以方便地关闭 JDBC 资源的各种 DbUnit.close()

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

Where to close a JDBC Connection while I want to return the ResultSet

Actually, you've almost answered that question yourself. As you experimented, closing the Connection will release the JDBC resources associated to it (at least, this is how things should work). So, if you want to return a ResultSet (I'll come back on this later), you need to close the connection "later". One way to do this would be obviously to pass a connection to your method, something like this:

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

The problem is that I don't really know what is your final goal and why you need so low level stuff so I'm not sure this is a good advice. Unless if you are writing a low level JDBC framework (and please, don't tell me you are not doing this), I would actually not recommend returning a ResultSet. For example, if you want to feed some business class, return some JDBC-independent object or a collection of them as other have advised instead of a ResultSet. Also bear in mind that a RowSet is a ResultSet so if you should not use a ResultSet then you should not use a RowSet.

Personally, I think you should use some helper class instead of reinventing the wheel. While Spring may be overkill and has a bit of learning curve (too much if you don't know it at all), Spring is not the only way to go and I strongly suggest to look at Commons DbUtils. More specifically, look at QueryRunner and especially this query() method:

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

As you can see, this method allows to pass a ResultSetHandler which exposes a callback method to convert ResultSets into other objects as described in z5h's answer and DbUtils provides several implementations, just pick up the one that will suit your needs. Also have a look at the utility methods of the DbUtils class, for example the various DbUnit.close() that you may find handy to close JDBC resources.

Really, unless you have very good reasons to do so (and I'd be curious to know them), don't write yet another JDBC framework, use an existing solution, it will save you some pain and, more important, some bugs and you'll benefit from proven good design. Even for low level stuff, there are existing (and simple) solutions as we saw. At least, check it out.

深海少女心 2024-08-22 03:40:47

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

从这些中,您可以编写一个 closeResultSet 实用程序方法,该方法将为您关闭所有 3 个方法,除了 ResultSet 之外什么都没有。

You can call ResultSet.getStatement to retrieve the Statement, and Statement.getConnection to retrieve the Connection.

From these you can write a closeResultSet utility method that will close all 3 for you, given nothing but the ResultSet.

情独悲 2024-08-22 03:40:47

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

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

The cleaner way is to use CachedRowSetImpl. But on MySQL 5.x+ there are some bugs with selecting columns by name or label.

For use with MySQL use this version:
https://stackoverflow.com/a/17399059/1978096

鸩远一方 2024-08-22 03:40:47

您确实不应该在较低级别处理 JDBC。使用像 spring 这样的框架,它会处理您所需的所有 close() 操作。

You really shouldn't handle with JDBC at the lower level. Use a framework like spring instead, it will handle all required close() operations for you.

ヤ经典坏疍 2024-08-22 03:40:47

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

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();
    }
}

I'd recommend that you do something more like this:

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();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文