Это предупреждение, которое выведет хибернейт, если для осуществления пагинации ему придется загрузить ВСЕ данные из таблицы, а не по одной странице.
🤔 Почему возникает?
Например, у нас есть две сущности: Post и Comment. Каждый пост может иметь множество комментариев:
@Entity public class Post { @Id private Long id; private String title; @OneToMany private List<Comment> comments; }
@Entity public class Comment { @Id private Long id; private String content; }
Посты мы достаем с помощью репозитория (делаем join fetch, чтобы подтянуть всё одним запросом):
public interface PostRepository extends JpaRepository<Post, Long> { @Query("SELECT p FROM Post p JOIN FETCH Comment c) List<Post> findAll(Pageable pageable); }
С точки зрения базы данные будут выглядеть примерно так:
p.id |
p.title |
c.id |
c.content |
1 |
пост1 |
1 |
коммент1 |
1 |
пост1 |
2 |
коммент2 |
2 |
пост2 |
3 |
коммент3 |
То есть, количество записей не будет совпадать с количество постов (на каждый пост будет приходиться столько записей, сколько у этого поста комментариев), из-за этого применить пагинацию на уровне sql не получится.
Хибернейт это понимает и ему приходится делать пагинацию на уровне кода, а для этого надо выгрузить все данные.
👨🔧 Как пофиксить?
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 offset :offset fetch first :limit rows only ) ) 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
ссылка на оригинал статьи https://habr.com/ru/articles/856092/
Добавить комментарий