【问题标题】:What is the idiomatic way to iterate over a result set without loading all results at once using Hibernate?在不使用 Hibernate 一次加载所有结果的情况下迭代结果集的惯用方法是什么?
【发布时间】:2013-09-07 23:51:03
【问题描述】:

假设我有一个这样的查询对象:

final SQLQuery query = createQuery(); // org.hibernate.SQLQuery
// HibernateTransaction is just a wrapper around Hibernate's Transaction, Session and
// SessionFactory classes

// the corresponding method:
private SQLQuery createQuery(HibernateTransaction t) {
    final SQLQuery query = t.fullSQLQuery(MY_QUERY_STRING);
    query.addScalar("column1", Hibernate.LONG);
    query.addScalar("column2", Hibernate.LONG);
    query.setResultTransformer(new AliasToBeanResultTransformer(MyDTO.class));
    return query;
}

SQLQuery 对象有一个名为iterate() 的方法。我的问题是,如果我尝试做这样的事情:

Iterator<MyDTO> iterator = query.iterate();
while(iterator.hasNext()) {
    MyDTO dto = iterator.next();
    // ...
}

我收到不支持迭代的异常。如果我使用query.list() 方法,那么我会得到一个List&lt;MyDTO&gt;,这很好,但如果我的查询返回一百万行,它会变慢并最终耗尽内存。

我的问题是,使用 Hibernate 一次只获取一行并懒惰地迭代它们的惯用方式是什么?在我的示例中,我尝试逐行处理表格中的数据。

我正在考虑使用聚集索引,并且只查询一次 1000 行,接下来的 1000 行将基于前 1000 行的最后一个 id(所以基本上我在考虑分页),但我们使用的 MyISAM 表不是支持这个。

【问题讨论】:

    标签: java mysql sql hibernate pagination


    【解决方案1】:

    您可以使用 SQLQuery 类中的 setMaxResults()setFirstResult() 方法。它们允许您模拟offsetlimit SQL 子句。

    如果您希望将结果从 rownum = 0 加载到 rownum = 50,例如,您可以使用 setFirstResult(0)setMaxResults(50) 等等。

    来源:Hibernate Javadocs

    【讨论】:

    • 因为setMaxResults()明确定义了查询最多返回的行数。
    • 我知道,但我怎么知道我必须重复这个过程多少次?例如:setFirstResult(1)setFirstResult(51) 等等。或者我只是运行一个单独的SELECT COUNT(*) FROM ... 查询?
    • 据我所知,您无法使用这些对象检索总行数,因此您需要单独的 SELECT COUNT 查询。
    【解决方案2】:

    是的,我们可以使用setMaxResults()setMaxResults()setFirstResult() 方法实现分页,你必须写类似

    // the corresponding method:
    private SQLQuery createQuery(HibernateTransaction t,int lowLimit,int pageSize) {
        final SQLQuery query = t.fullSQLQuery(MY_QUERY_STRING);
        query.addScalar("column1", Hibernate.LONG);
        query.addScalar("column2", Hibernate.LONG);
        query.setFirstResult(lowLimit);
        query.setMaxResults(pageSize);
        query.setResultTransformer(new AliasToBeanResultTransformer(MyDTO.class));
        return query;
    }
    

    循环如下

    int pageSize = 10;
    int lowLimit = 0;
    while(lowLimit != -1) {
        int size;
        List<MyDTO> list = query.list();
        size = list.size();
        //prosess your list here
        processResult(list);    
        if(pageSize != size) {
            lowLimit = -1;  
        } else {
          lowLimit = lowLimit + pageSize;   
        }
    }
    

    所以这个循环将首先获取 10 条记录,然后将计数器增加 10,这个循环将继续,直到所有记录都没有被获取,因为我不确定结果,因为我没有测试过这段代码,但应该可以工作......

    【讨论】:

      猜你喜欢
      • 2016-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多