5.1 Различные формы связи один-к-одному
Есть еще один интересный и довольно специфический случай отношений между двумя Entity-классами – это отношение один-к-одному.
Я называю этот случай очень специфическим, так как это больше о Java-объектах, чем о базе данных. В базе данных есть всего два варианта связи между таблицами:
- Строка таблицы содержит ссылку на id другой таблицы.
- Служебная таблица используется для связи many-to-many.
В случае же с Entity-классами могут быть варианты, которые описываются несколькими аннотациями:
- @Embedded
- Односторонний OneToOne
- Двусторонний OneToOne
- @MapsId
Ниже мы рассмотрим самые популярные из них.
5.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'
5.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
.
5.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();
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ