@OneToOne

Відкрита

1. Різні форми зв'язку один до одного

Є ще один цікавий і досить специфічний випадок відносин між двома Entity-класами — це відношення один-до-одного.

Я називаю цей випадок дуже специфічним, оскільки це більше про Java-об'єкти, ніж про базу даних. У базі даних є лише два варіанти зв'язку між таблицями:

  • Рядок таблиці містить посилання на id іншої таблиці.
  • Службова таблиця використовується для зв'язку many-to-many.

У випадку з Entity-класами можуть бути варіанти, які описуються кількома анотаціями:

  • @Embedded
  • Односторонній OneToOne
  • Двосторонній OneToOne
  • @MapsId

Нижче ми розглянемо найпопулярніші з них.

2. @Embedded

Найпростіший варіант зв'язку one-to-one ми, до речі, вже розглянули — це анотація @Embedded. У цьому випадку у нас два класи зберігаються в одній таблиці.

Припустимо, ми хочемо зберігати адресу користувача в класі UserAddress:

@Embeddable
class UserAddress {
   @Column(name="user_address_country")
   public String country;
   @Column(name="user_address_city")
   public String city;
   @Column(name="user_address_street")
   public String street;
   @Column(name="user_address_home")
   public String home;
}

Тоді нам потрібно просто додати поле з цією адресою до класу User:

@Entity
@Table(name="user")
class User {
   @Column(name="id")
   public Integer id;

   @Embedded
   public UserAddress address;

   @Column(name="created_date")
   public Date createdDate;
}

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

Приклад HQL-запиту:

select from User where address.city = 'Paris'

3. Односторонній OneToOne

Уявимо тепер ситуацію: у нас є вихідна таблиця employee і task, який посилається на employee. Але ми точно знаємо, що на одного користувача може бути призначено максимум одне завдання. Тоді для опису цієї ситуації ми можемо скористатися анотацією @OneToOne.

Приклад:

@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;

   @Column(name="name")
   public String description;

   @OneToOne
   @JoinColumn(name = "employee_id")
   public Employee employee;

   @Column(name="deadline")
   public Date deadline;
}

Hibernate стежитиме за тим, щоб не тільки в одного завдання був один користувач, а й щоб у одного користувача було лише одне завдання. Загалом цей випадок практично нічим не відрізняється від @ManyToOne.

4. Двосторонній OneToOne

Попередній варіант може бути трохи незручним, тому що часто хочеться не просто привласнити завдання співробітника, а й співробітнику призначити завдання.

Для цього можна додати поле EmployeeTask до класу Employee та виставити для нього правильні анотації.

@Entity
@Table(name="employee")
class Employee {
   @Column(name="id")
   public Integer id;

   @OneToOne(cascade = CascadeType.ALL, mappedBy="employee")
   private EmployeeTask task;
}

Важливо! У таблиці employee немає поля task_id, натомість для встановлення зв'язку між таблицями використовується поле employee_id таблиці task.

Встановлення зв'язку між об'єктами виглядає так:

Employee director = session.find(Employee .class, 4);
EmployeeTask task = session.find(EmployeeTask . class, 101);
task.employee = director;
director.task = task;

session.update(task);
session.flush();

Для видалення зв'язку посилання також потрібно видалити в обох об'єктів:

Employee director = session.find(Employee .class, 4);
EmployeeTask task = director.task;

task.employee = null;
session.update(task);

director.task = null;
session.update(director);

session.flush();
1
Задача
Модуль 4. Робота з БД,  13 рівень4 лекція
Недоступна
OneToOne
task1307
Коментарі (2)
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
22 березня, 15:18
Розібралася де наплутала наплутала
22 березня, 10:31
Вітаю, шукала інфу про @MapsId. Маю проблему: при Односторонній OneToOne із використанням ще додатково @MapsId, все ок допоки роблю лише одну запис, а при додаванні ще одного employee виникають проблеми - попередні дані employee затираються новим і по суті створюєть лише одна запис в цю таблицю. Спершу треба створювати запис для task,а потім для employee, якщо в них спільне поле id при @MapsId? Чи взагалі наплутала. Було б чудово, якби доповнили і для випадку з @MapsId :)