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

undefined
1
Задача
Модуль 4. Работа с БД, 13 уровень, 4 лекция
Недоступна
OneToOne
Есть два класса-энтити: User и Address. Таблицы, которые им соответствуют...