Hibernate pagination mechanism
I am using in query and in hibernate call back. both are working as expected. Hibernate Query executes for results in between First and Max size given. Here Seems like you passed SQL not HQL to query. if yes it shouldn't work.
-- See my code here.
Query query = this.getSession().createQuery("FROM QueryType");
query.setFirstResult(0);
query.setMaxResults(20);
List toDelete = query.list();
and in log:
select * from ( select -- ALL column names. (dont want to share here.) from MY_TBL_NAME querytype0_ ) where rownum <= ?
There are quite a few ways to paginate.
HQL and 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 and the 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;
Simply the 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 lists them all with examples.
You can use the JPA pagination for both entity queries and native SQL.
To limit the underlying query ResultSet
size, the JPA Query
interface provides the setMaxResults
method.
Navigating the following page requires positioning the result set where the last page ended. For this purpose, the JPA Query
interface provides the 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 projection queries
The JPA query pagination is not limited to entity queries that return entities only. You can use it for DTO projections as well.
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();
Native SQL queries
The JPA query pagination is not limited to entity queries, such as JPQL or Criteria API. You can use it for native SQL queries as well.
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 and pagination
However, if we try to use the JOIN FETCH
clause in the entity query while also using JPA pagination:
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 will issue the following warning message:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
And the executed SQL query will lack the pagination clause:
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
This is because Hibernate wants to fetch entities fully along with their collections as indicated by the JOIN FETCH
clause while the SQL-level pagination could truncate the ResultSet
possibly leaving a parent Post
entity with fewer elements in the comments
collection.
The problem with the HHH000104
warning is that Hibernate will fetch the product of Post
and PostComment
entities, and due to the result set size, the query response time is going to be significant.
In order to work around this limitation, you have to use a Window Function query:
@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"),
}
)
}
)
For more details about using Window Functions to fix the
HHH000104
issue as well as the code forDistinctPostResultTransformer
.