Hibernate ScrollableResults 不返回整个结果集

Posted

技术标签:

【中文标题】Hibernate ScrollableResults 不返回整个结果集【英文标题】:Hibernate ScrollableResults Do Not Return The Whole Set of Results 【发布时间】:2011-02-04 09:09:41 【问题描述】:

我们运行的一些查询有 100000 多个结果,加载它们然后将它们发送到客户端需要很长时间。所以我使用 ScrollableResults 来获得分页结果功能。但我们以大约 50k 个结果(从不完全相同的结果数量)达到最高。

我在 Oracle9i 数据库上,使用 Oracle 10 驱动程序,Hibernate 配置为使用 Oracle9 方言。我尝试使用最新的 JDBC 驱动程序(ojdbc6.jar),问题重现。

我们也跟着一些advice添加了排序子句,但是问题重现了。

这里有一个代码 sn-p 来说明我们的工作:

final int pageSize = 50;
Criteria crit = sess.createCriteria(ABC.class);
crit.add(Restrictions.eq("property", value));
crit.setFetchSize(pageSize);
crit.addOrder(Order.asc("property"));
ScrollableResults sr = crit.scroll();
...
...
ArrayList page = new ArrayList(pageSize);
do
  for (Object entry : page)
    sess.evict(entry); //to avoid having our memory just explode out of proportion
  page.clear();
  for (int i =0 ; i < pageSize && ! metLastRow;  i++)
    if (sr.next())
      page.add(sr.get(0));
    else 
      metLastRow = true;
  
  metLastRow = metLastRow?metLastRow:sr.isLast();

  sendToClient(page);
while(!metLastRow);

那么,为什么我得到的结果集在最后告诉我它什么时候应该有更多的结果?

【问题讨论】:

【参考方案1】:

您的代码 sn-p 缺少重要部分,例如 resultSetpage 的定义。但我想知道无论如何,不​​应该这条线

if (resultSet.next())

宁可

if (sr.next())

?

顺便说一句,AFAIK 从持久化上下文中清除多余的对象可以通过调用来实现

session.flush();
session.clear();

而不是循环遍历对象集合以分别逐出每个对象。 (当然,这要求查询在其自己的独立会话中执行。)

更新:好的,下一轮猜测:-)

您能否真正检查发送到客户端的行并将其与直接针对 DB 的等效 SQL 查询的结果进行比较?很高兴知道此代码是否检索(并将所有行发送到客户端,直到某个限制,或者仅从整个结果集中检索一些行(如每 2 行),或者......这可以阐明根原因。

你可以尝试的另一件事是

crit.setFirstResults(0).setMaxResults(200000);

【讨论】:

你对第一部分是正确的。我改进了代码 sn-p。这是从两个类中提取的东西,我将它们合并在一起以使其更具可读性。 您对session.flush(); 部分也是正确的。问题是,由于代码是实用程序类的一部分,我想确保代码的其他部分不会有任何副作用。拨打 session.evict() 看起来更安全。 我跟踪了Hibernate生成的SQL语句并单独运行它,我能够检索到所有100'000+行 @mlaverd 很好,现在你能按照我上面的建议来比较结果吗? 我运行了 Hibernate 执行的相同查询,它获取了所有 100'000+ 个结果。我比较了结果,也没有跳过任何内容。【参考方案2】:

由于我在基于List&lt;E&gt; 实例的大型项目代码中遇到了同样的问题, 我编写了一个非常有限的 List 实现,仅支持迭代器来浏览 ScrollableResults 而不重构所有服务实现和方法原型。

此实现在我的IterableListScrollableResults.java Gist 中可用

它还会定期从会话中刷新 Hibernate 实体。这是一种使用它的方法,例如,当从 DB 中将所有非归档实体导出为带有 for 循环的文本文件时:

Criteria criteria = getCurrentSession().createCriteria(LargeVolumeEntity.class);
criteria.add(Restrictions.eq("archived", Boolean.FALSE));
criteria.setReadOnly(true);
criteria.setCacheable(false);
List<E> result = new IterableListScrollableResults<E>(getCurrentSession(),
        criteria.scroll(ScrollMode.FORWARD_ONLY));
for(E entity : result) 
    dumpEntity(file, entity);

希望对你有所帮助

【讨论】:

以上是关于Hibernate ScrollableResults 不返回整个结果集的主要内容,如果未能解决你的问题,请参考以下文章

Spring和Hibernate的注解整合 hibernate3和hibernate4/5的区别

hibernate.merge()方法怎么用

hibernate 异常 怎么解决

Hibernate之Hibernate环境配置

(转)Hibernate框架基础——Hibernate API及Hibernate主配置文件

Hibernate基础学习—Hibernate相关API介绍