存储在 hashMap 中的结果集给出零行计数

Result set stored in hashMap giving zero row count

我的 class 中有两个方法,首先我调用方法 dbExecuteStatement(),它执行 sql 查询。执行 sql 查询后,我得到一个 ResultSet 对象。我将此 ResultSet 对象保存在静态 hashMap 中,以便在我的下一个方法调用 fetchResults() 中,我可以使用现有的结果集来检索结果。将 ResultSet 对象保存在映射中的原因是,在 fetchResults() 方法请求参数中,我将获得最大提取行大小,并根据该值迭代结果集。这两种方法都应该从客户端单独调用。

现在我面临的问题是,当我在 fetchResults() 方法中迭代 ResultSet 对象时,我得到的行计数为零。如果我从 dbExecuteStatement() 中的 hashMap 获取相同的结果集,我会得到实际的行数,即在我的例子中是 5。我检查了我在 fetchResults() 方法和 dbExecuteStatement() 中放入散列映射的 ResultSet 对象,它是同一个对象。但是如果在 fetchResults() 方法和 dbExecuteStatement() 中获取 ResultSetMetaData 对象,它们是不同的。有人可以帮助我理解原因,为什么我得到的结果计数为零。

代码如下:

  public class HiveDao1 {
    private static Map<Object,Map<Object,Object>> databaseConnectionDetails 
        = new HashMap<Object,Map<Object,Object>>();

    //This method will execute the sql query and will save the ResultSet obj in a hashmap for later use
     public void dbExecuteStatement(DbExecuteStatementReq dbExecuteStatementReq){
      //I already have a connection object saved in map
      String uniqueIdForConnectionObject = dbExecuteStatementReq.getDbUniqueConnectionHandlerId();
      Map<Object,Object> dbObject = databaseConnectionDetails.get(uniqueIdForConnectionObject);
      Connection connection = (Connection) dbObject.get(DatabaseConstants.CONNECTION);

      try {

    Statement stmt = connection.createStatement() ;
        // Execute the query
    ResultSet resultSet = stmt.executeQuery(dbExecuteStatementReq.getStatement().trim()) ;

    //save the result set for further use, Result set will be used in fetchResult() call
    dbObject.put(DatabaseConstants.RESULTSET, resultSet);

    /*
     * Now below is the debugging code,which I put to compare the result set
     *  iteration dbExecuteStatement() and fetchResults method
     */
    ResultSet rs = (ResultSet) dbObject.get(DatabaseConstants.RESULTSET);

    ResultSetMetaData md = (ResultSetMetaData) dbObject.get(DatabaseConstants.RESULTSETMETADATA);

     System.out.println("==ResultSet fethced in dbExecuteStatement=="+rs);
     System.out.println("==ResultSet metadata fetched in dbExecuteStatement ==="+rs.getMetaData());

    int count = 0;
    while (rs.next()) {
        ++count;
    }

     if (count == 0) {
        System.out.println("No records found");
    }

    System.out.println("No of rows found from result set in dbExecuteStatement is "+count);

    } catch (SQLException e) {
    e.printStackTrace();
    }

}

     /*
      * This method fetch the result set object from hashMap
      * and iterate it on the basis of fetch size received in req parameter
      */
public void fetchResults(FetchResultsReq fetchResultsReq){

    String uniqueIdForConnectionObject = fetchResultsReq.getDbUniqueConnectionHandlerId();
    Map<Object,Object> dbObject = databaseConnectionDetails.get(uniqueIdForConnectionObject);

    try {
        //Fetch the ResultSet object that was saved by dbExecuteStatement()
        ResultSet rs = (ResultSet) dbObject.get(DatabaseConstants.RESULTSET);
        ResultSetMetaData md = (ResultSetMetaData) dbObject.get(DatabaseConstants.RESULTSETMETADATA);

        System.out.println("ResultSet fethced in fetchResults at server side dao layer======"+rs);
        System.out.println("ResultSet metadata fetched in fetchResults at server side dao layer======"+md);

       int count = 0;
       while (rs.next()) {
           ++count;
        }

        if (count == 0) {
         System.out.println("No records found");
        }
        //Here the row count is not same as row count in dbExecuteStatement()            
        System.out.println("No of rows found from result set in fetchResults is "+count);

    } catch (SQLException e) {
    e.printStackTrace();
    }
    }

}

扩展我的评论(和@Glenn 的):

多次使用 ResultSet

当您编写迭代 ResultSet 的调试代码时,光标会移动到结果的末尾。当然,如果你再调用同一个对象并使用next(),它仍然在最后,所以你不会再得到任何记录。

如果您确实需要多次读取同一个 ResultSet,则需要执行查询,使其 returns 成为可滚动的 ResultSet。您在创建语句时执行此操作:

Statement stmt = connection.createStatement( 
                              ResultSet.TYPE_SCROLL_INSENSITIVE,
                              ResultSet.CONCUR_READ_ONLY );

connection.createStatement() 创建的不带参数的默认语句 returns 类型为 ResultSet.TYPE_FORWARD_ONLY 的结果集,并且 ResultSet 对象只能读取一次。

如果您的结果集类型是滚动不敏感或滚动敏感的,您可以使用像rs.first()这样的语句来重置游标,然后您可以再次获取记录。

将语句保持在范围内

@Glenn 的评论非常重要。你的程序现在的工作方式,它可能在整个测试阶段都运行良好,然后突然在生产中,你的 ResultSet 中有时会有零记录,并且错误只会偶尔重现 - 调试噩梦.

如果产生 ResultSetStatement 对象被关闭,那么 ResultSet 本身也被关闭。由于您没有自己关闭 Statement 对象,因此这将在 Statement 对象完成时完成。

stmt 变量是本地变量,它是我们所知道的对 Statement 的唯一引用。因此,它将被垃圾收集器回收。然而,具有终结器的对象被降级到终结队列,并且无法知道何时调用终结器,也无法控制它。一旦发生这种情况,ResultSet 就会变得无法控制。

因此请务必在 ResultSet 旁边保留对语句对象的引用。并确保在使用完 ResultSet 后自行正确关闭它,并且不会再使用它。关闭它后,请记住删除您为语句和结果集保留的引用,以避免内存泄漏。关闭很重要,依赖终结器是一个糟糕的策略。如果您不自己关闭它,您可能 运行 在数据库中的某个时间点没有游标(取决于 DBMS 及其配置)。