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. Но об этом мы расскажем немного позже.

undefined
1
Задача
Модуль 4. Работа с БД, 10 уровень, 2 лекция
Недоступна
Расстановка аннотаций
Есть три класса-энтити: Author, Book и Publisher. Расставь в них аннотации, учитывая, что Author и Publisher являются полями Book. Для упрощения считаем, что у каждой книги может быть только один автор.
undefined
1
Задача
Модуль 4. Работа с БД, 10 уровень, 2 лекция
Недоступна
task1005
В методе main получи сессию из MySessionFactory.getSessionFactory(). Напиши запрос на hql для получения книг из таблицы book...