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