Hibernate分页机制

时间:2013-10-29 03:36:41

标签: java hibernate jpa orm pagination

我正在尝试将Hibernate分页用于我的查询(PostgreSQL)

我为我的sql查询设置了setFirstResult(0),setMaxResults(20)。我的代码如下:

Session session = getSessionFactory().getCurrentSession();
session.beginTransaction();
Query query = session.createQuery("FROM Customers");
query.setFirstResult(0);
query.setMaxResults(20);
List<T> entities = query.list();
session.getTransaction().commit();

但是在查看SQL hibernate日志时,我仍然看到完整的SQL查询:

Hibernate: select customer0_.id as id9_, customer0_.customer_name as dst2_9_, customer0_.addres as dst3_9_ from tbl_customers customer0_  

为什么查询Hibernate分页SQL日志时没有LIMIT OFFSET?

有没有人知道Hibernate分页机制?

我想Hibernate将选择所有数据,将数据放入Resultset,然后在Resultset中进行分页,对吗?

3 个答案:

答案 0 :(得分:7)

有很多方法可以分页。

HQL和setFirstResult,setMaxResults API

Session session = sessionFactory.openSession();
Query query = session.createQuery("From Foo");
query.setFirstResult(0);
query.setMaxResults(10);
List<Foo> fooList = query.list();
//Total count
String countQ = "Select count (f.id) from Foo f";
Query countQuery = session.createQuery(countQ);
Long countResults = (Long) countQuery.uniqueResult();
//Last Page
int pageSize = 10;
int lastPageNumber = (int) ((countResult / pageSize) + 1);

HQL和ScrollableResults API

String hql = "FROM Foo f order by f.name";
Query query = session.createQuery(hql);
int pageSize = 10;

ScrollableResults resultScroll = query.scroll(ScrollMode.FORWARD_ONLY);
resultScroll.first();
resultScroll.scroll(0);
List<Foo> fooPage = Lists.newArrayList();
int i = 0;
while (pageSize > i++) {
    fooPage.add((Foo) resultScroll.get(0));
    if (!resultScroll.next())
        break;
}
//Total count
resultScroll.last();
int totalResults = resultScroll.getRowNumber() + 1;

仅限Criteria API

Criteria criteria = session.createCriteria(Foo.class);
criteria.setFirstResult(0);
criteria.setMaxResults(pageSize);
List<Foo> firstPage = criteria.list();
//Total count
Criteria criteriaCount = session.createCriteria(Foo.class);
criteriaCount.setProjection(Projections.rowCount());
Long count = (Long) criteriaCount.uniqueResult();

baeldung列出了所有示例。

答案 1 :(得分:4)

我在查询和hibernate回调中使用。两者都按预期工作。 Hibernate Query在给定的First和Max大小之间执行结果。这里好像你通过SQL而不是HQL来查询。如果是的话它应该不起作用。

- 请在此处查看我的代码。

        Query query = this.getSession().createQuery("FROM QueryType");
        query.setFirstResult(0);
        query.setMaxResults(20);
        List toDelete = query.list();

并在日志中:

选择         *     从         ( 选择              - 所有列名称。 (不想在这里分享。)         从             MY_TBL_NAME querytype0_)     哪里         rownum&lt; =?

答案 2 :(得分:2)

正如我在this article中所述,您可以将JPA分页用于实体查询和本机SQL。

为限制基础查询ResultSet的大小,JPA Query接口提供了setMaxResults method

导航下一页需要将结果集放置在最后一页的结束位置。为此,JPA Query接口提供了setFirstResult method

JPQL

List<Post> posts = entityManager
.createQuery(
    "select p " +
    "from Post p " +
    "order by p.createdOn ")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

DTO投影查询

JPA查询分页不限于仅返回实体的实体查询。您也可以将其用于DTO投影。

List<PostCommentSummary> summaries = entityManager
.createQuery(
    "select new " +
    "   com.vladmihalcea.book.hpjp.hibernate.fetching.PostCommentSummary( " +
    "       p.id, p.title, c.review " +
    "   ) " +
    "from PostComment c " +
    "join c.post p " +
    "order by c.createdOn")
.setMaxResults(10)
.getResultList();

本地SQL查询

JPA查询分页不限于实体查询,例如JPQL或Criteria API。您也可以将其用于本机SQL查询。

List<Tuple> posts = entityManager
.createNativeQuery(
    "select p.id as id, p.title as title " +
    "from post p " +
    "order by p.created_on", Tuple.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

加入筹码和分页

但是,如果我们尝试在实体查询中使用JOIN FETCH子句,同时还使用JPA分页:

List<Post> posts = entityManager.createQuery(
    "select p " +
    "from Post p " +
    "left join fetch p.comments " +
    "order by p.createdOn", Post.class)
.setMaxResults(10)
.getResultList();

Hibernate将发出以下警告消息:

HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!

执行的SQL查询将缺少分页子句:

SELECT p.id AS id1_0_0_,
       c.id AS id1_1_1_,
       p.created_on AS created_2_0_0_,
       p.title AS title3_0_0_,
       c.created_on AS created_2_1_1_,
       c.post_id AS post_id4_1_1_,
       c.review AS review3_1_1_,
       c.post_id AS post_id4_1_0__,
       c.id AS id1_1_0__
FROM post p
LEFT OUTER JOIN post_comment c ON p.id=c.post_id
ORDER BY p.created_on

这是因为Hibernate希望按照JOIN FETCH子句的说明完全获取实体及其集合,而SQL级分页可能会截断ResultSet并可能留下父Post实体comments集合中的元素较少。

HHH000104警告的问题在于,Hibernate将获取PostPostComment实体的乘积,并且由于结果集的大小,查询响应时间将为重要。

要解决此限制,您必须使用Window Function查询:

@NamedNativeQuery(
    name = "PostWithCommentByRank",
    query =
        "SELECT * " +
        "FROM (   " +
        "    SELECT *, dense_rank() OVER (ORDER BY \"p.created_on\", \"p.id\") rank " +
        "    FROM (   " +
        "        SELECT p.id AS \"p.id\", " +
        "               p.created_on AS \"p.created_on\", " +
        "               p.title AS \"p.title\", " +
        "               pc.id as \"pc.id\", " +
        "               pc.created_on AS \"pc.created_on\", " +
        "               pc.review AS \"pc.review\", " +
        "               pc.post_id AS \"pc.post_id\" " +
        "        FROM post p  " +
        "        LEFT JOIN post_comment pc ON p.id = pc.post_id " +
        "        WHERE p.title LIKE :titlePattern " +
        "        ORDER BY p.created_on " +
        "    ) p_pc " +
        ") p_pc_r " +
        "WHERE p_pc_r.rank <= :rank ",
    resultSetMapping = "PostWithCommentByRankMapping"
)
@SqlResultSetMapping(
    name = "PostWithCommentByRankMapping",
    entities = {
        @EntityResult(
            entityClass = Post.class,
            fields = {
                @FieldResult(name = "id", column = "p.id"),
                @FieldResult(name = "createdOn", column = "p.created_on"),
                @FieldResult(name = "title", column = "p.title"),
            }
        ),
        @EntityResult(
            entityClass = PostComment.class,
            fields = {
                @FieldResult(name = "id", column = "pc.id"),
                @FieldResult(name = "createdOn", column = "pc.created_on"),
                @FieldResult(name = "review", column = "pc.review"),
                @FieldResult(name = "post", column = "pc.post_id"),
            }
        )
    }
)
  

有关使用Window Functions修复HHH000104问题以及DistinctPostResultTransformer的代码的更多详细信息,请查看this article