Warning: mkdir(): No space left on device in /var/www/tgoop/post.php on line 37

Warning: file_put_contents(aCache/aDaily/post/java_fillthegaps/--): Failed to open stream: No such file or directory in /var/www/tgoop/post.php on line 50
Java: fill the gaps@java_fillthegaps P.597
JAVA_FILLTHEGAPS Telegram 597
Как найти и починить in-memory пагинацию в Spring Data JPA

N+1 - самая известная проблема в Spring Data JPA/Hibernate, но не единственная. В этом посте расскажу об in-memory пагинации.

В чем суть.

Пагинацию логично делать на уровне БД с помощью limit и offset. Но иногда Hibernate делает пагинацию на стороне клиента. Выгружает все записи из память, выбирает из них заданное количество и возвращает.

Чем плохо:
Запрос выполняется долго, тк по сети передается куча данных
Забивается оперативная память. В худшем случае получаем OutOfMemoryError

Проблема возникает при сочетании предварительной загрузки связных сущностей и Pageable.

Простой пример. У постов есть комментарии со связью OneToMany:
@Entity
public class Post {
   @OneToMany
   private List<Comment> comments;
}

Мы хотим получить 5 постов с комментариями. Чтобы избежать N+1, загружаем комментарии через EntityGraph:
@EntityGraph
List<Post> findAllPosts(PageRequest.of(0, 5));

Метод findAllPosts вернёт 5 постов, как мы и просили. Но в логах увидим SQL запрос без лимитов, в память загрузятся все посты.

Как найти места со скрытой пагинацией?

Hibernate пишет в консоль ворнинг, который легко пропустить. Лучше сделать так:
🌷 Ставим свойство
spring.jpa.properties.hibernate.query.fail_on_pagination_over_collection_fetch=true

🌷 Запускаем интеграционные тесты
🌷 Смотрим, где падают исключения

Как починить скрытую пагинацию?

Способов много, самое простое - добавить над списком @BatchSize. Либо поставить свойство
spring.jpa.properties.hibernate.default_batch_fetch_size=50

Тогда BatchSize будет по умолчанию применяться для всех списков.
Про другие решения и перфоманс читайте в этой статье на Хабре

Зачем Hibernate использует in-memory пагинацию?

При запросе с джойном из нескольких таблиц получается декартово произведение, для которого некорректно применить limit. Но контракт соблюдать надо хоть как-то, поэтому была выбрана пагинация на стороне клиента.

Сразу возникает вопрос. Почему в таких случаях не сделать 2 запроса?
▫️ select * from posts limit 5;
▫️ Извлечь айдишники постов
▫️ select * from comments where post_id in (?,?,?,?,?);
▫️ Связать сущности

Решение выглядит просто, и вариант с BatchSize примерно так и работает. Почему оно не используется по умолчанию - непонятно.

Hibernate - яркий пример “протекающих абстракций”. Куча нюансов и проблем, о которых нужно знать. В документации по поводу in-memory панинации есть такая фраза:

Possibility of terrible performance is left as a problem for the client to avoid.
Проблема ужасного перформанса - это проблема клиента.

Вся суть хибернейта🤌

По возможности не тащите его в проект. Есть более удобные и прозрачные альтернативы. Например, Spring Data JDBC💚
🔥155👍7131👎5



tgoop.com/java_fillthegaps/597
Create:
Last Update:

Как найти и починить in-memory пагинацию в Spring Data JPA

N+1 - самая известная проблема в Spring Data JPA/Hibernate, но не единственная. В этом посте расскажу об in-memory пагинации.

В чем суть.

Пагинацию логично делать на уровне БД с помощью limit и offset. Но иногда Hibernate делает пагинацию на стороне клиента. Выгружает все записи из память, выбирает из них заданное количество и возвращает.

Чем плохо:
Запрос выполняется долго, тк по сети передается куча данных
Забивается оперативная память. В худшем случае получаем OutOfMemoryError

Проблема возникает при сочетании предварительной загрузки связных сущностей и Pageable.

Простой пример. У постов есть комментарии со связью OneToMany:

@Entity
public class Post {
   @OneToMany
   private List<Comment> comments;
}

Мы хотим получить 5 постов с комментариями. Чтобы избежать N+1, загружаем комментарии через EntityGraph:
@EntityGraph
List<Post> findAllPosts(PageRequest.of(0, 5));

Метод findAllPosts вернёт 5 постов, как мы и просили. Но в логах увидим SQL запрос без лимитов, в память загрузятся все посты.

Как найти места со скрытой пагинацией?

Hibernate пишет в консоль ворнинг, который легко пропустить. Лучше сделать так:
🌷 Ставим свойство
spring.jpa.properties.hibernate.query.fail_on_pagination_over_collection_fetch=true

🌷 Запускаем интеграционные тесты
🌷 Смотрим, где падают исключения

Как починить скрытую пагинацию?

Способов много, самое простое - добавить над списком @BatchSize. Либо поставить свойство
spring.jpa.properties.hibernate.default_batch_fetch_size=50

Тогда BatchSize будет по умолчанию применяться для всех списков.
Про другие решения и перфоманс читайте в этой статье на Хабре

Зачем Hibernate использует in-memory пагинацию?

При запросе с джойном из нескольких таблиц получается декартово произведение, для которого некорректно применить limit. Но контракт соблюдать надо хоть как-то, поэтому была выбрана пагинация на стороне клиента.

Сразу возникает вопрос. Почему в таких случаях не сделать 2 запроса?
▫️ select * from posts limit 5;
▫️ Извлечь айдишники постов
▫️ select * from comments where post_id in (?,?,?,?,?);
▫️ Связать сущности

Решение выглядит просто, и вариант с BatchSize примерно так и работает. Почему оно не используется по умолчанию - непонятно.

Hibernate - яркий пример “протекающих абстракций”. Куча нюансов и проблем, о которых нужно знать. В документации по поводу in-memory панинации есть такая фраза:

Possibility of terrible performance is left as a problem for the client to avoid.
Проблема ужасного перформанса - это проблема клиента.

Вся суть хибернейта🤌

По возможности не тащите его в проект. Есть более удобные и прозрачные альтернативы. Например, Spring Data JDBC💚

BY Java: fill the gaps


Share with your friend now:
tgoop.com/java_fillthegaps/597

View MORE
Open in Telegram


Telegram News

Date: |

More>> 5Telegram Channel avatar size/dimensions The imprisonment came as Telegram said it was "surprised" by claims that privacy commissioner Ada Chung Lai-ling is seeking to block the messaging app due to doxxing content targeting police and politicians. How to create a business channel on Telegram? (Tutorial) How to Create a Private or Public Channel on Telegram?
from us


Telegram Java: fill the gaps
FROM American