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 缺少重要部分,例如 resultSet
和 page
的定义。但我想知道无论如何,不应该这条线
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<E>
实例的大型项目代码中遇到了同样的问题,
我编写了一个非常有限的 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的区别