休眠分页或批处理
Posted
技术标签:
【中文标题】休眠分页或批处理【英文标题】:Hibernate pagination or batch processing 【发布时间】:2013-03-03 16:59:55 【问题描述】:问题:如何一次处理(读入)1000 条记录的批次,并确保只有当前批次的 1000 条记录在内存中?假设我的主键名为“ID
”,而我的表名为Customer
。
背景:这不是用于用户分页,而是用于编译有关我的表格的统计信息。我的可用内存有限,因此我想一次读取 1000 条记录的记录。我只是在阅读记录,它们不会被修改。我读过StatelessSession
适合这种事情,我听说有人使用ScrollableResults
。
我尝试过的内容: 目前我正在开发一个定制的解决方案,在该解决方案中我实现了 Iterable,并且基本上使用 setFirstResult
和 setMaxResults
进行了分页。这对我来说似乎很慢,但它允许我一次获得 1000 条记录。我想知道如何更有效地做到这一点,也许像ScrollableResults
这样的东西。我还不确定为什么我目前的方法这么慢;我按 ID 排序,但 ID 是主键,所以表应该已经被索引了。
如您所知,我一直在阅读有关如何执行此操作的零碎文章。如果有人可以为我提供一个完整的方法来做到这一点,将不胜感激。我知道你必须在ScrollableResults
上设置FORWARD_ONLY
并且调用evict(entity)
会占用一个实体的内存(除非你正在做二级缓存,我还不知道如何检查我是否是或不)。但是,我在 JavaDoc 中看不到任何方法可以一次读取 1000 条记录。我希望在可用内存不足和网络性能缓慢之间取得平衡,因此一次通过网络发送一条记录真的不是这里的选择。我尽可能使用 Criteria API。感谢您的详细回复。
【问题讨论】:
旁注,如果有人能指出 cmets 中“一次滚动浏览记录 X”的适当术语,将不胜感激。我认为它被称为批处理,但 Hibernate 文档仅在其批处理部分中提到更新和删除... 这是一种批处理/分页的形式,只是它可能不是一个很常见的请求。我需要这样做,最后只是获取内存中的所有 PK 并执行大量where Id in (...)
查询。
另外,仅仅因为您的表由 PK 索引并不一定意味着记录在内部由 PK 排序。该表可能使用可扩展散列索引。即使这听起来不太可能,B+-树似乎更常见。此外,查询可能会在某个时候使用与 PK 不同的索引进行过滤,这意味着之后仍需要对结果进行重新排序。 (例如,如果您选择日期范围内的记录,它们将从按日期排序的表中检索。) EXPLAIN 应该清除它。
@millimoose oracle 文档似乎另有说明(关于 PK 始终是索引)
我并不是说 PK 没有被索引。我是说它可能不是在执行查询时首先访问的索引。 (这将在应用排序之前确定元素的顺序。)
【参考方案1】:
可以使用 oracle 的 ROWNUM 特性来帮助你。 假设我们需要获取表 CUSTOMERS 的 1000 行 (pagesize),并且我们需要获取第二页 (pageNumber)
创建和调用这样的查询可能是答案
select * from
(select rownum row_number,customers.* from Customer
where rownum <= pagesize*pageNumber order by ID)
where row_number >= (pagesize -1)*pageNumber
【讨论】:
如果在这个过程中添加了行,那可能会抛出 rownum,对吗?需要注意的事项。 如果您的某些实体可以使用 Native Query,否则必须在您的基础 DataAcess 层实现中实现。 @Marvo:不,Oracle 管理查询结果不干扰当前添加的行 --- 请参阅 stanford.edu/dept/itss/docs/oracle/10g/server.101/b10743/… 上的“语句级读取一致性”@ @M.Heydari 标题说明了一切。 “语句级”。获取后续块将发生在单独的语句中。 (当然,交易可能会处理得很好。) 所以每个查询的正确性将由隔离级别来保证,而在使用order by ID时会保证整体语句的正确性【参考方案2】:以只读方式加载实体。
HQL
Query.setReadOnly( true );
对于标准
Criteria.setReadOnly( true );
http://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/readonly.html#readonly-api-querycriteria
Stateless session 与 State-Session 完全不同。
使用无状态会话执行的操作永远不会级联到关联的实例。无状态会话会忽略集合
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/batch.html#batch-statelesssession
使用 flash() 和 clear() 清理会话缓存。
session.flush();
session.clear();
Question about Hibernate session.flush()
ScrollableResults 应该如您所愿。
不要忘记您加载的每个项目都会占用内存空间,除非您驱逐或清除并需要检查它是否确实有效。
mysql J/Connecotr 中的 ScrollableResults 是假的,它会加载整行,但我认为 oracle 连接器工作正常。
Using Hibernate's ScrollableResults to slowly read 90 million records
如果你找到替代方案,你可以考虑使用这种方式
1. Select PrimaryKey of every rows that you will process
2. Chopping them into PK chunk
3. iterate -
select rows by PK chunk (using in-query)
process them what you want
【讨论】:
@Transactional
是 Spring 特定的,在帖子/标签中的任何地方都没有提到。
将实体加载为只读解决了什么问题?如何一次抓取 1000 条记录?它是否设置了获取大小? hibernate 文档的 fetch size 部分指的是关联而不是其他任何东西。还是 setFirstResult 和 setMaxResults?如果是,那我为什么要使用 ScrollableResults 呢?以上是关于休眠分页或批处理的主要内容,如果未能解决你的问题,请参考以下文章
在 apollo-react 问题中默认获取更多分页或仅网络策略
在尝试进行基于 ajax 的分页或过滤时,在局部视图中呈现的 WebGrid 将引发““jQuery is not defined””