RuntimeException - Exhausted Resultset - 它怎么会发生?

时间:2013-12-13 10:44:11

标签: java multithreading exception resultset

代码:

    // Start the query.
    final ResultSet r = prepared.executeQuery();
    try {
      // Returning false from oneRow will stop the process.
      boolean goOn = true;
      while (r.next() && goOn) {
        log.trace("Result: {}", toString(r));
        // We did do at least one.
        ran = true;
        goOn = oneRow(r);
      }
    } finally {
      try {
        // Always remember to close the ResultSet.
        r.close();
      } catch (SQLException ex) {
        log.error("Close failed", ex);
      }
    }

    // Handle one row.
    public boolean oneRow(ResultSet r) throws Exception {
      String xml1 = r.getString(1);
      String xml2 = r.getString(2);
      if (xml1 == null && xml2 == null) {
        // Probably compressed.
        xml1 = decompress(r, 3);
        xml2 = decompress(r, 4);
      }
      return false;
    }

  private static String decompress(ResultSet rs, int col) throws SQLException {
    // Exception gets thrown here!!! ???
    final InputStream compressed = rs.getBinaryStream(col);
    ...

正如您所看到的,这不是不调用ResultSet.next()的明显问题。不仅如此,我已经在getString两次调用ResultSet,这是第三次打破它。

请注意,这是一个偶然的问题,此代码在大多数情况下都能正常运行。

查询类似于:

"SELECT P1.XML XML1, "
      + "P2.XML XML2, "
      + "P1.CompressedXML CompressedXML1, "
      + "P2.CompressedXML CompressedXML2 "
      + "FROM Table1 P1 "
      + "LEFT JOIN Table2 T2 ON T2.ID = P1.ID "
      + "LEFT JOIN Table1 P2 ON P2.ID = T2.Item_Code "
      + "WHERE P1.ID = ?"

我意识到这是一个相当乱伦的查询,但正如我所说 - 这一切都在大多数情况下都很好。

user1933888发布的答案提示我发现,由于我使用的是本地连接池,因此准备好的语句可能会在共享不同连接的两个线程之间干扰自身吗?

我确信同一个连接永远不会被两个线程同时使用,但是准备好的语句可以共享,因为它应该驻留在数据库中。

1 个答案:

答案 0 :(得分:1)

由于您的PreparedStatement链接到一个连接,如果您在线程之间共享PreparedStatement,您最终将共享一个连接,这肯定会导致问题。

确实,语句通常缓存在DB中以避免重新解析它,但是每个线程仍然需要一个PreparedStatement对象,以避免混淆DB驱动程序关于什么线程想要什么。

一般情况下,考虑到一般AppServer为您做池或(如果您不在AppServer中)有很多连接池库,我会避免使用家用Brew连接池,所以为什么重新发明轮子。

一般来说:

  • 您可以在并发线程之间共享DataSource
  • 您不应在并发线程之间共享Connection,PreparedStatement或ResultSet对象

连接池将确保非并发线程可以重用Connections,而JDBC驱动程序将在内部缓存语句

希望这是有道理的。