【问题标题】:Hibernate pagination mechanism休眠分页机制
【发布时间】:2013-11-08 01:48:17
【问题描述】:

我正在尝试对我的查询使用 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中分页,对吧?

【问题讨论】:

  • Nope Hibernate 不会这样做,它会尝试在查询级别应用分页。我相信这是一个方言问题,你确定你在 Hibernate config 中设置了 Postgre 方言吗?
  • 顺便问一下,这是 SQL 还是 HQL?您必须将 HQL 传递给 createQuery 方法。
  • 在这个例子中,我使用的是:Hibernate Query Examples (HQL)
  • 问题解决了吗?

标签: java hibernate jpa orm pagination


【解决方案1】:

我在查询和休眠回调中使用。两者都按预期工作。 Hibernate Query 对给定的 First 和 Max size 之间的结果执行。这里似乎您通过 SQL 而不是 HQL 进行查询。如果是,它不应该工作。

-- 在这里查看我的代码。

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

并在日志中:

选择 * 从 ( 选择 -- 所有列名。 (不想在这里分享。) 从 MY_TBL_NAME 查询类型0_ ) 在哪里 行数

【讨论】:

  • 我使用的是 Hibernate 查询示例 (HQL)(我更新了我的代码),而不是 Native SQL。它仍然显示所有日志查询。
【解决方案2】:

分页方式有很多种。

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 用示例将它们全部列出。

【讨论】:

  • 您使用 ScrollableResults API 的示例会将所有内容读入内存。 See here。您可以按照@Sean S. 的解释使其工作,但它绝对不能提供简单的解决方案。
  • 依赖关系数据库并且不是所有关系数据库都有这个问题。很高兴知道,但这不是原始发帖人的问题/担忧
【解决方案3】:

您可以将 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
    """, Post.class)
.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<Post> posts = entityManager.createQuery("""
    select p
    from Post p
    left join fetch p.comments
    where p.title like :titlePattern
    order by p.createdOn
    """, Post.class)
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setMaxResults(5)
.getResultList();

JOIN FETCH 和分页

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

List<Post> posts = entityManager.createQuery("""
    select p
    from Post p
    left join fetch p.comments
    where p.title like :titlePattern
    order by p.createdOn
    """, Post.class)
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setMaxResults(5)
.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
WHERE p.title LIKE :titlePattern
ORDER BY p.created_on

这是因为 Hibernate 想要完全获取实体及其集合,如 JOIN FETCH 子句所示,而 SQL 级别的分页可能会截断 ResultSet,可能会留下一个父 Post 实体在 @ 中的元素较少987654340@收藏。

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.post_id AS "pc.post_id",
                    pc.id as "pc.id", pc.created_on AS "pc.created_on",
                    pc.review AS "pc.review"
                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"),
            }
        )
    }
)

有关使用窗口函数修复HHH000104 问题的更多详细信息以及DistinctPostResultTransformer 的代码。

【讨论】:

    猜你喜欢
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    • 2010-12-27
    相关资源
    最近更新 更多