Java for Beginner
710 subscribers
624 photos
172 videos
12 files
984 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Как я память искал (Часть II)

В предыдущей части мы рассмотрели составные части возникновения проблемы.

А теперь давайте рассмотрим, как решить данный кейс и как не попасть в подобную ловушку неочевидного поведения Hibernate.

Решения 🤓

🟡Первое и самое очевидное:

Избегать fetch join при пагинации в Hibernate

Да так просто.

Но вы скажете: "А как же проблема N+1"? Ее я предлагаю решить всем известной аннотацией
@BatchSize.
@Entity
public class User {
//стандартные поля
@OneToMany(mappedBy = "owner")
@BatchSize(size = 50)
private List<Car> cars = new ArrayList<>();
}
Количество запросов существенно уменьшится — на тот размер size, который мы задали. Это значит, что проблема N+1 решена.


Да, запросов станет точно больше чем 1. Но такова цена использования Spring Hibernate 🤷‍♀️

🟡Второе и не очень хорошее, а при увеличении количества связанных сущностей в запросе - опасное:

разделение запроса на две части с использованием @EntityGraph

@EntityGraph(attributePaths = {"cars"})
Page<User> findAll(Pageable pageable);


При таком решении в первой части запроса мы запрашиваем id наших User с определенной страницы. А во второй части запроса с помощью @EntityGraph и JOIN мы получаем все Car которые им принадлежат.

Но как показали тестовые запуски с использованием @EntityGraph над множеством связанных сущностей в основной, проведенные автором статьи(спасибо ему) :
"При пяти загружаемых коллекциях производительность разделенного запроса с @EntityGraph хуже в 2 раза. При десяти — в 45 раз. А при загрузке 15 коллекций — в 1401 раз! На графике нет данных по разделенному запросу для 20 коллекций, так как я просто-напросто получил ошибку OutOfMemoryError."

С @BatchSize же никаких JOIN не происходит, и при добавлении дополнительной коллекции с ассоциацией @OneToMany просто добавляются дополнительные select для этой коллекции.


🟡И третье, элементарное как свет солнца 😏

Использовать Нативный SQL - запрос.

Да, больше мороки с формированием динамического SQL (если у тебя 10 разных фильтров, ты сам будешь собирать SQL строку).
@Query(value = """
SELECT u.id AS userId, u.name AS userName, c.model AS carModel
FROM user u
LEFT JOIN car c ON c.owner_id = u.id
WHERE (:name IS NULL OR u.name ILIKE %:name%)
ORDER BY
CASE WHEN :sortBy = 'name' THEN u.name
WHEN :sortBy = 'id' THEN CAST(u.id AS TEXT)
ELSE u.name
END ASC
LIMIT :limit OFFSET :offset
""", nativeQuery = true)
List<UserCarDTO> findWithFilters(
@Param("name") String name,
@Param("sortBy") String sortBy,
@Param("limit") int limit,
@Param("offset") int offset
);


Да, придется использовать дополнительный count-запрос: SELECT COUNT(*) , для обеспечения пагинации
@Query(value = """
SELECT COUNT(DISTINCT u.id)
FROM user u
WHERE (:name IS NULL OR u.name ILIKE %:name%)
""", nativeQuery = true)
long countUsers(@Param("name") String name);


но, ты сам полностью управляешь процессом и скорее всего не встретишь неочевидного поведения 💪

И на сладкое

Какие ещё конструкции в JPA/Hibernate могут привести к аналогичным проблемам, когда ты используешь fetch join и неожиданно получаешь избыточную загрузку в память, ломающую пагинацию или вызывающую дублирование/нагрузку?

1. Pageable + @Query (с JPQL) и JOIN FETCH
Даже если ты не используешь Specification, а пишешь JPQL с @Query, проблема остаётся.

2. EntityManager + JPQL с fetch и пагинацией
Даже ручной вызов через EntityManager может привести к тому же эффекту.

3. Criteria API с fetch + .setMaxResults()/.setFirstResult()
Даже если ты используешь чистый JPA Criteria API, проблема может проявиться.

4. Spring Data REST + @RepositoryRestResource + fetch join
Если ты используешь Spring Data REST, и в репозитории включён fetch join, например через @Query, то Spring сам применяет Pageable, и может попасть в эту ловушку. Опять же — всё сломается.


И личный совет: изучайте матчасть! Читайте наш канал и шанс, что Ваш прод упадет - существенно снизится!

Понравился стиль подачи материала?

Отправь другу и ставь -
🔥

#Java #fetch #autor
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6