@ManyToOne

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

2.1 Связь на уровне таблиц

Мы познакомились с тем, как Hibernate хранит коллекции во вспомогательных таблицах. Теперь же разберемся, как организовать связи между полноценными таблицами, которые хранят настоящие Entity-классы.

Всего между Entity-классами в Hibernate есть четыре вида отношений:

  • one-to-one
  • one-to-many
  • many-to-one
  • many-to-many

И начнем мы разбор с самого простого варианта – many-to-one.

С таким отношением между таблицами в SQL ты уже сталкивался. Вот как оно обычно выглядит:

id name occupation salary age join_date
1 Иванов Иван Программист 100000 25 2012-06-30
2 Петров Петр Программист 80000 23 2013-08-12
3 Иванов Сергей Тестировщик 40000 30 2014-01-01
4 Рабинович Мойша Директор 200000 35 2015-05-12
5 Кириенко Анастасия Офис-менеджер 40000 25 2015-10-10
6 Васька Кот 1000 3 2018-11-11

Таблица employee:

В этой таблице есть такие колонки:

  • id INT
  • name VARCHAR
  • occupation VARCHAR
  • salary INT
  • age INT
  • join_date DATE

А так выглядит таблица task, которая содержит задачи для сотрудников:

id employee_id name deadline
1 1 Исправить багу на фронтенде 2022-06-01
2 2 Исправить багу на бэкенде 2022-06-15
3 5 Купить кофе 2022-07-01
4 5 Купить кофе 2022-08-01
5 5 Купить кофе 2022-09-01
6 (NULL) Убрать офис (NULL)
7 4 Наслаждаться жизнью (NULL)
8 6 Наслаждаться жизнью (NULL)

В этой таблице есть всего 4 колонки:

  • id – уникальный номер задания (и строки в таблице);
  • employee_id – ID сотрудника из таблицы employee, на которого назначена задача;
  • name – название и описание задачи;
  • deadline – время до которого нужно выполнить задачу.

Мы видим, что много строк таблицы task могут ссылаться на одну запись таблицы employee. Такая связь на уровне таблиц называется многие-к-одному (many-to-one).

2.2 Связь на уровень Java-классов

Кроме связи на уровне таблиц, можно еще организовать связь на уровне Entity-классов в Hibernate. Делается это с помощью аннотации @ManyToOne.

Но для начала просто создадим два класса: Employee и EmployeeTask:


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

И второй класс для хранения заданий сотрудников:


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

С этими классами все хорошо, но между ними нет никакой связи, которая бы отражала тот факт, что поле employeeId класса EmployeeTask ссылается на поле id класса Employee. Пришло время это исправить

2.3 Аннотация @ManyToOne.

Во-первых, в Java мы привыкли оперировать объектами (и ссылками на объекты), а не их id. Так что первым делом давайте вместо поля employeeId в классе EmployeeTask просто укажем ссылку на объект типа Employee. Вот как будет выглядеть наш новый класс:


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

С помощью аннотации @ManyToOne мы указали, что много объектов EmployeeTask могут ссылаться на один объект типа Employee. Также с помощью аннотации @JoinColumn мы указали, в какой колонке нашей таблицы хранится id объекта Employee.

2.4 Примеры запросов

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

Сценарий первый

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

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

Ты можешь просто обращаться к полям зависимых классов через точку. Это очень удобно. Но давай все же запишем этот запрос в виде Java-кода:


String hql = "from EmployeeTask where employee.name = :username";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameter("username", "Иван Иванович");
List<EmployeeTask> resultLIst = query.list();

Сценарий второй

Давай напишем запрос, который возвращает список сотрудников, у которых есть просроченные задачи. Задача просрочена, если ее deadline уже в прошлом. Вот как будет выглядеть этот запрос на 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.


Employee director = session.get(Employee.class, 4);
 
String hql = "update EmployeeTask set employee = :user where employee is null";
Query<EmployeeTask> query = session.createQuery(hql, EmployeeTask.class);
query.setParameter("user", director);
query.executeUpdate();

1
Задача
Модуль 4. Работа с БД, 13 уровень, 1 лекция
Недоступна
Опять книги
Есть три класса-энтити: Author, Book и Publisher, и таблицы, которые им соответствуют
Комментарии (11)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
I'm Siberian Уровень 109 Expert
20 февраля 2025
Чёт запутался. у каждой книги один автор один издатель. первая мысль была ванТуВан потом понял. пишу OneToMany - как понимаю - один автор - много книг, но почему ManyToOne - много авторов одна книга? принцип oneToMany manyToOne не понял. в каком случае как. почему такой порядок. или имеется ввиду ManyToOne - много книг к одному автору? как магистр йода завещал? "нипанятнааааа" В след лекции читаем: На одну запись таблицы employee могут ссылаться много строк таблицы task. Такая связь на уровне таблиц называется один-ко-многим (one-to-many). всё, я сломался
Евгений Уровень 97
3 февраля 2025
Без алиасов валидатор не принимает решение.
Ольга Николенко Уровень 109 Expert
6 июня 2024
пихнула на автомате название колонки в бд вместо поля класса, пол часа не могла найти где ошибка 🤪
Надежда Уровень 104 Expert
29 сентября 2023
Без псевдонима (alias'a) для сущности Book валидатор не принимает решение.
Александр Уровень 112
3 апреля 2025
спасибо, долго бы колупался, чего валидатор не принимает))
Артём Уровень 112
12 июля 2025
У меня оно без алисаов и работать тоже не хотело, эксепшены выкидывало.
Сергей Уровень 108 Expert
8 сентября 2023
@ManyToOne получает много полей из класса к которому коннектится, для одного поля текущего класса. у нас в примере было в таблице "task" @JoinColumn(name = "employee_id") public Employee employee; на самом деле вместо employee_id тут может быть написано что угодно хоть @JoinColumn(name = "vasya"). Hibernate автоматически коннектит по полю ID(по первичному ключу) если не указано иначе. Если нужно соединяться по другому полю то так @JoinColumn(name = "vasya", referencedColumnName = "имя поля класса Employee")
Андрей Уровень 109
4 июля 2024
да вроде вася не подходит. это же внешний ключ, его надо указать.
aDuVaN4Ik Уровень 42
18 января 2023
Так и не понял что делает аннотация @MatyToOne
Марат Гарипов Уровень 108 Expert
12 февраля 2023
даёт hibernate понять каким образом связаны энтити из разных таблиц
Руслан Никитин Уровень 109
2 февраля 2025
Аннотация @ManyToOne говорит Hibernate, что несколько экземпляров текущей сущности могут ссылаться на один экземпляр Employee.