@RomanMaras

Как эффективно использовать HQL при запросах сущностей с множеством связей?

Во время работы над учебным проектом получилась следующая схема в БД:
63c533ae65a17576328836.png
Всё как у людей: есть аккаунт, у него может быть несколько карт, а по каждой из карт есть входящие и исходящие транзакции.
Наверняка часто будет возникать ситуация, когда нужно для конкретного аккаунта (который мы определяем, например, по айди) выгрузить сразу все карты, а для каждой карты под шумок подгрузить входящие и исходящие транзакции. В проекте я использую Spring Data JPA, Но в данном случае я не могу просто запросить аккаунт с помощью метода JPA: вылетает hibernate.LazyInitializationException, а EagerLoad я использовать не хочу, вроде антипаттерн же

Что ж, попробуем написать ручками на HQL:
@Transactional
    public List<Card> findByOwnerId(int id) {
        List<Card> cards = entityManager
                .createQuery("select distinct c " +
                        "from Card c " +
                        "left join fetch c.inputTransactions " +
                        "where c.ownerAccount.id = :id", Card.class)
                .setParameter("id", id)
                .setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false)
                .getResultList();
        cards = entityManager
                .createQuery("select distinct c " +
                        "from Card c " +
                        "left join fetch c.outputTransactions " +
                        "where c in :cards", Card.class)
                .setParameter("cards", cards)
                .setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false)
                .getResultList();
        return cards;
    }


Тут всего два обращения к EntityManager: в первую очередь выгружаем все карты и сразу же джоиним на входящие транзакции, а вторым запросом подгружаем исходящие транзакции. Итак, при вызове метода мы вроде должны получить всего два запроса к БД, однако на практике (в логах Hibernate) я обнаружил целых пять (и это на случае, где у аккаунта всего одна карта и по ней есть три исходящие и две входящие транзакции): два из них – те самые корректные с джоином таблиц card и transaction , ещё два запроса – обращение к таблице account (без джоинов) и ещё один – странный запрос с джоином таблиц card и account по совпадению account_id.

Так вот, наконец, вопрос: я всё-таки делаю что-то не так, и при большом количестве карт и транзакций мой сервис захлебнется от переизбытка запросов, которые будет генерить Hibernate, либо я всё делаю верно и сдобно? Если разбираетесь, посоветуйте best practice для таких ситуаций
  • Вопрос задан
  • 113 просмотров
Пригласить эксперта
Ответы на вопрос 1
mayton2019
@mayton2019 Куратор тега Java
Bigdata Engineer
У меня - сходу замечание по твоему стилю. Зачем ты переменную card переписываешь? Это сбивает с толку.
List<Card> cards = .....

cards = entityManager

Нельзя одну переменную брать в двух ипостасях. Сэкономил в одном - проиграл в читаемости.

Второе я думаю что эта задача прекрасно решается одним SQL-запросом. Так было в продуктовых системах с 2000х годов когда еще не было этих ваших ORM/Hibernate. И все нормально работало. Поэтому делай все одним запросом. Не думай о накладных расходах в базе. Мой опыт показывает что база - лучше справляется когда выбирает все данные сразу одним курсором (запросом).

А игры с Lazy-Eager которые придумали в ORM решают проблемый самого ORM и ApplicationServer а базе они вобщем-то не нужны.

Если ты собрался глубоко заняться оптимизацией - посмотри лекцию Алименкова особенно в части трассировки Hibernate запросов. Собери цифры. Сколько карточек на 1 акк в среднем? 1 или 10 или 1000? Сколько транзакций на акк? Это все влияет на смыслы оптимизаций.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
Bell Integrator Ульяновск
До 400 000 ₽
Bell Integrator Хабаровск
До 400 000 ₽
Bell Integrator Ижевск
До 400 000 ₽
19 апр. 2024, в 20:43
20000 руб./за проект
19 апр. 2024, в 20:11
500 руб./за проект