Решаем проблему пагинации в памяти

📄 Решаем проблему пагинации в памяти

1. Использовать два отдельных запроса
Первый запрос вытащит id нужных нам постов и именно к этому запросу будет применена пагинация.
Второй вытащит уже сами сущности, к этому запросу пагинацию не применяем.

2. Использовать подзапрос

Логика такая же, как в первом пункте, но айдишники мы найдем подзапросом, а не отдельно:
@Query("""
select p
from Post p
left join fetch p.comments
where p.id in (
select id
from (
select id,
from Post
order by id
limit :limit
offset :offset
)
)
order by p.id
"""
)
List<Post> findAll(int offset, int limit);

3. Использовать Blaze
Blaze предоставляет более удобное апи для построение запрсов через код:
public List<Post> findAll() {
return criteriaBuilderFactory
.create(entityManager, Post.class)
.fetch("comments")
.orderBy("id", true)
.page(
(int) pageRequest.getOffset(),
pageRequest.getPageSize()
)
.withCountQuery(false)
.getResultList();
}
Блейз достаточно умный и поймет, что в этом случае надо сделать подзапрос:
SELECT
p1_0.id,
p1_0.title
c1_0.id,
c1_0.post_id,
c1_0.content
FROM
post p1_0
LEFT JOIN
post_comment c1_0 ON p1_0.id=c1_0.post_id
WHERE p1_0.id in (
SELECT
p2_0.id
FROM
post p2_0
ORDER BY
p2_0.id ASC
FETCH FIRST 25 ROWS ONLY
)
ORDER BY
p1_0.id ASC

Также, можно попросить хибернейт кидать исключение при in-memory пагинации:
spring.jpa.properties.hibernate.query.fail_on_pagination_over_collection_fetch=true

👨‍💻 Джуниор