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 .