Join в HQL

Модуль 4. Работа с БД
10 уровень , 2 лекция
Открыта

3.1 Маппинг зависимых сущностей

В SQL можно писать запросы с использованием JOIN. А можно ли так же делать в HQL? Краткий ответ – да. А вот полный ответ будет более интересным.

Во-первых, когда мы пишем JOIN в SQL, то чаще всего это значит, что одна таблица у нас ссылается на другую таблицу. Например, таблица task содержит колонку employee_id, которая ссылается на колонку id таблицы employee.

Эту зависимость можно описать с помощью аннотаций в Hibernate. Для начала давай просто создадим Entities для наших таблиц. Сначала опишем таблицу employee:


@Entity @Table(name="employee")
class Employee {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String name;
 
   @Column(name="salary")
   public Integer salary;
 
   @Column(name="join_date")
   public Date joinDate;
}

И класс EmployeeTask для таблицы task:


@Entity @Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String name;
 
   @Column(name="employee_id")
   public Integer employeeId;
 
   @Column(name="deadline")
   public Date deadline;
}

Все отлично, но есть одно предложение. Давай посмотрим на поле employeeId в последнем примере:


@Column(name="employee_id")
public Integer employeeId;

Ничего странного не замечаешь? Если нет, то это значит, что у тебя уже сформировалось мышление на языке SQL.

Все дело в том, что в языке Java, мы обычно описываем такую зависимость немного по-другому:


public Employee employee;

Нам не нужно указывать id, мы обычно сразу указываем переменную, которая хранит ссылку на объект Employee. Или хранит null, если такого объекта нет.

И Hibernate позволяет нам описать такую ситуацию с помощью аннотаций:


@ManyToOne @JoinColumn(name="employee_id", nullable=true)
public Employee employee;

Аннотация @ManyToOne говорит Hibernate, что много сущностей EmployeeTask могут ссылаться на одну сущность Employee.

А аннотация @JoinColumn указывает имя колонки, из которой будет браться id. Вся остальная необходимая информация будет взята из аннотаций класса Employee.

Итоговый результат будет выглядеть так:


@Entity @Table(name="task")
class EmployeeTask
{
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String name;
 
   @ManyToOne @JoinColumn(name="employee_id", nullable=true)
   public Employee employee;
 
   @Column(name="deadline")
   public Date deadline;
}

3.2 Использование join в HQL

А теперь разберем, как писать запросы к связанным сущностям на HQL.

Первая ситуация.

У нас есть сотрудник (Employee) и мы хотим получить список его задач. Вот как будет выглядеть этот запрос на SQL:


SELECT task.* FROM task JOIN employee ON task.employee_id = employee.id
WHERE employee.name = "Иван Иванович";

А теперь запишем этот же запрос на HQL:


from EmployeeTask where employee.name = "Иван Иванович"

У класса EmployeeTask есть поле employee, а у него есть поле name, так что такой запрос будет работать.

Ситуация вторая.

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


SELECT DISTINCT employee.*
FROM task JOIN employee ON task.employee_id = employee.id
WHERE task.deadline < CURDATE();

DISTINCT используется, потому что может быть много задач назначенных на одного пользователя.

А теперь запишем этот же запрос на HQL:


select distinct employee from EmployeeTask where deadline < current_date();

employee в этом запросе – это поле класса EmployeeTask

Ситуация третья.

Назначим все неназначенные задачи на директора. Запрос на SQL будет выглядеть так:


UPDATE task SET employee_id = 4 WHERE employee_id IS NULL

А теперь запишем этот же запрос на HQL:


update EmployeeTask set employee = :user where employee is null

Последний запрос самый сложный. Нам нужно передать ID, директора, но класс EmployeeTask не содержит поля, куда можно записать id, вместо этого он содержит поле Employee, куда нужно присвоить ссылку на объект типа Employee.

В Hibernate эта проблема решается с помощью параметров к запросу, которые передаются в объект Query. А в самом HQL такие параметры записываются через двоеточие: :user. Но об этом мы расскажем немного позже.

1
Задача
Модуль 4. Работа с БД, 10 уровень, 2 лекция
Недоступна
Расстановка аннотаций
Есть три класса-энтити: Author, Book и Publisher. Расставь в них аннотации, учитывая, что Author и Publisher являются полями Book. Для упрощения считаем, что у каждой книги может быть только один автор.
1
Задача
Модуль 4. Работа с БД, 10 уровень, 2 лекция
Недоступна
task1005
В методе main получи сессию из MySessionFactory.getSessionFactory(). Напиши запрос на hql для получения книг из таблицы book...
Комментарии (7)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Олег Уровень 106 Expert
12 октября 2024
Кто-то в курсе, почему мы ставим точки тут: SELECT task.* FROM task JOIN employee ON task.employee_id = employee.id ?
Virgilion Уровень 41
1 декабря 2024
Чтобы указать к какой таблице относится " * ". Если бы ты хотел получить только колонку employee_id из таблицы task, ты бы писал task.employee_id А раз мы хотим вывести все колонки, то просто заменяем employee_id на * Надеюсь понятно объяснил 😂
Ольга Уровень 108 Expert
6 сентября 2023
В 1005 задаче в условиях издатель должен называться "Фолио", а ответ принимается только если издатель = "Chatto & Windus"
7 марта 2023
без указания @Id не работает!
Юрий Суботинов Уровень 92 Expert
31 января 2023
Если кто тестит задачи через свои базы данных и сталкивается с ошибками типа: java.lang.ClassNotFoundException: com.fasterxml.jackson.core.JsonFactory caused by upgrading jackson-core Нужно установить библиотеки jackson
Сергей Уровень 82
30 сентября 2025
Спасибо, помог совет ))
Stas S Уровень 108 Expert
30 ноября 2022

select distinct employee from EmployeeTask where deadline < CURDATE();
Должно быть "current_date()" HQL doc