JPA / Hibernate 有没有一种滚动结果的方式?

12

我在Toplink中发现了一些提示

Query query = em.createQuery("SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC");
query.setHint("eclipselink.cursor.scrollable", true);
ScrollableCursor scrollableCursor = (ScrollableCursor)query.getSingleResult();
List<Employee> emps = scrollableCursor.next(10);

是否有 JPA/Hibernate 的替代品?

5个回答

12
据我所知,JPA中没有标准的解决方法。
使用Hibernate时,我所知道的最接近的替代方法是Query / ScrollableResults API。从文档中可以看到:

10.4.1.6. Scrollable iteration

If your JDBC driver supports scrollable ResultSets, the Query interface can be used to obtain a ScrollableResults object that allows flexible navigation of the query results.

Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
                            "order by cat.name");
ScrollableResults cats = q.scroll();
if ( cats.first() ) {

    // find the first name on each page of an alphabetical list of cats by name
    firstNamesOfPages = new ArrayList();
    do {
        String name = cats.getString(0);
        firstNamesOfPages.add(name);
    }
    while ( cats.scroll(PAGE_SIZE) );

    // Now get the first page of cats
    pageOfCats = new ArrayList();    
    cats.beforeFirst();    
    int i=0;    
    while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );

}

cats.close()

Note that an open database connection and cursor is required for this functionality. Use setMaxResult()/setFirstResult() if you need offline pagination functionality.


2
不要忘记将close()放入try ... finally中。 - Hasan Tuncay

9
从其他答案来看,JPA不直接支持滚动,但如果您使用Hibernate作为JPA实现,则可以进行滚动。
javax.persistence.Query query = entityManager.createQuery("select foo from bar");
org.hibernate.Query hquery = query.unwrap(org.hibernate.Query);
ScrollableResults results = hquery.scroll(ScrollMode.FORWARD_ONLY);

这个访问底层Hibernate API进行滚动操作,但你可以使用所有JPA查询的功能。(至少对于标准查询,JPA API具有一些旧版Hibernate API中没有的功能。)


4
当处理大型项目编码基于 List<E> 实例的大量实体时,我不得不编写一个非常有限的 List 实现,仅支持 Iterator 以浏览 ScrollableResults,而无需重构所有使用 List<E> 的服务实现和方法原型。
这个实现可以在我的 IterableListScrollableResults.java Gist 中找到。
它还定期从会话中清除 Hibernate 实体。例如,以下是一种使用它的方式,用于导出所有非存档实体作为文本文件,并使用 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);
}

希望这能有所帮助


1
在JPA中,您可以使用query.setFirstResult和query.setMaxResults。

5
那不是滚动。每次都会发出新的查询,这样会增加额外开销。另外,您需要按某种方式进行排序,以获得可靠的分页,这也会增加查询时间。不幸的是,这似乎是获得类似滚动的功能(尽管性能可能不佳)的唯一方法。 - extraneon
1
如果您要在正在添加/删除数据的在线数据库上“滚动”,并且添加/删除操作发生在页面边界附近,那么您可能会跳过或重复获取一些记录... - Sergey Ushakov

0

同时使用Spring Data也是一种选择。在那里,您可以指定查询并传递“PageRequest”作为参数,在其中指定页面大小和页码:

Page<User> users = repository.findAll(new PageRequest(1, 20));

为此,您需要扩展PagingAndSortingRepository。

这是另一种在结果上进行分页的替代方法。

当然,在底层,它使用Hibernate、Toplink或任何您配置的JPA实现。


这绝对不是想要的结果。Pageable会创建多个请求。可滚动的结果是单个请求,它保持结果集打开,但仅加载小批量到RAM中。而且,超出前几页的页面请求会在一定程度上对数据库造成压力(取决于实际查询计划和连接数量、排序条件等)。 - Bogdan Mart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接