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;
}

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 < ; CURDATE();

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. Але про це ми розповімо трохи згодом.