Перейти к основному содержимому

Отдельные запросы в HQL

· 3 мин. чтения

1. Обзор

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

2. Понимание проблемы

Во-первых, давайте посмотрим на нашу модель данных и определим, чего мы пытаемся достичь.

Мы будем использовать объекты сущностей Post и Comment , которые имеют отношение «один ко многим». Мы хотим получить список сообщений со всеми связанными с ними комментариями.

Начнем со следующего HQL-запроса:

String hql = "SELECT p FROM Post p LEFT JOIN FETCH p.comments";
List<Post> posts = session.createQuery(hql, Post.class).getResultList();

Это сгенерирует следующий SQL-запрос:

select
post0_.id as id1_3_0_,
comment2_.id as id1_0_1_,
post0_.title as title2_3_0_,
comment2_.text as text2_0_1_,
comments1_.Post_id as Post_id1_4_0__,
comments1_.comments_id as comments2_4_0__
from
Post post0_
left outer join
Post_Comment comments1_
on post0_.id=comments1_.Post_id
left outer join
Comment comment2_
on comments1_.comments_id=comment2_.id

Результат будет содержать дубликаты. Сообщение будет показано столько раз, сколько связанных с ним комментариев — сообщение с тремя комментариями появится в списке результатов три раза.

3. Использование Different в HQL-запросе

Нам нужно будет использовать ключевое слово в нашем HQL-запросе, чтобы исключить дубликаты:

String hql = "SELECT DISTINCT p FROM Post p LEFT JOIN FETCH p.comments";
List<Post> posts = session.createQuery(hql, Post.class).getResultList();

Теперь мы получаем правильный результат: больше нет повторяющихся объектов Post . Давайте посмотрим на инструкцию SQL, сгенерированную Hibernate:

select
distinct post0_.id as id1_3_0_,
comment2_.id as id1_0_1_,
post0_.title as title2_3_0_,
comment2_.text as text2_0_1_,
comments1_.Post_id as Post_id1_4_0__,
comments1_.comments_id as comments2_4_0__
from
Post post0_
left outer join
Post_Comment comments1_
on post0_.id=comments1_.Post_id
left outer join
Comment comment2_
on comments1_.comments_id=comment2_.id

Мы можем заметить, что ключевое слово different не только использовалось Hibernate, но также было включено в SQL-запрос. Мы должны избегать этого, потому что это не нужно и вызовет проблемы с производительностью.

4. Использование QueryHint для прекращения передачи отдельного ключевого слова

Начиная с Hibernate 5.2, мы можем использовать механизм сквозной передачи, чтобы больше не передавать отдельное предложение HQL/JPQL оператора SQL .

Чтобы отключить pass-distinct-through , нам нужно добавить в запрос подсказку QueryHint.PASS_DISTINCT_THROUGH со значением false :

String hql = "SELECT DISTINCT p FROM Post p LEFT JOIN FETCH p.comments";
List<Post> posts = session.createQuery(hql, Post.class)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();

Если мы проверим результат, мы увидим, что нет дублирующихся сущностей. Кроме того, в операторе SQL не использовалось отдельное предложение:

select
post0_.id as id1_3_0_,
comment2_.id as id1_0_1_,
post0_.title as title2_3_0_,
comment2_.text as text2_0_1_,
comments1_.Post_id as Post_id1_4_0__,
comments1_.comments_id as comments2_4_0__
from
Post post0_
left outer join
Post_Comment comments1_
on post0_.id=comments1_.Post_id
left outer join
Comment comment2_
on comments1_.comments_id=comment2_.id

5. Вывод

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

Как всегда, исходный код доступен на GitHub .