@OneToOne

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

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

1
Задача
Модуль 4. Работа с БД, 13 уровень, 4 лекция
Недоступна
OneToOne
Есть два класса-энтити: User и Address. Таблицы, которые им соответствуют...
Комментарии (11)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Руслан Никитин Уровень 109
5 февраля 2025
mappedBy = "employee" указывает, что связь управляется полем employee в классе EmployeeTask. Это значит, что EmployeeTask является владельцем этой связи. @MapsId - позволяет установить связь между двумя сущностями, где одна из них ссылается на другую, а их идентификаторы совпадают
Руслан Никитин Уровень 109
5 февраля 2025

@Entity
@Table(name = "employee")
class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

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

@Entity
@Table(name = "employee_task")
class EmployeeTask {
    @Id
    private Integer id; // Идентификатор будет совпадать с идентификатором Employee

    @MapsId // Указывает, что этот идентификатор совпадает с идентификатором Employee
    @OneToOne
    @JoinColumn(name = "id") // Связывает с полем id в Employee
    private Employee employee;

    private String address;
    private String phoneNumber;
}

Роман Уровень 88
14 января 2025
что то я совсем теряться начал
Олег Уровень 106 Expert
15 октября 2024
Сложно.
Nikita Shamrai Уровень 8 Expert
30 ноября 2022
Появляется новый параметр mappedBy про которого ни слова в лекции. Связи между таблицами показаны очень ненаглядно.
Джама Уровень 108 Expert
9 января 2023
Много чего непонятно. Мне статья помогла
Nazar Уровень 9
28 сентября 2023
Це не дуже коректна стаття
Эльдар Уровень 108 Expert
8 октября 2023
Как всегда в лекции одно, а в решении из разряда "Придумай сам что-то новое, но чтобы работало"
Александр Уровень 83
3 ноября 2023
Такова участь разрабов... на работе что-то похожее ожидает, я думаю, может нас таким образом нативно к этому готовят
Олег Шукюров Уровень 41
19 декабря 2023
Немного сумбурно просто описали, mappedBy используется в случае явного указания сущности в которой будет указана связь. В нашем случае: class Employee { @OneToOne(cascade = CascadeType.ALL, mappedBy="employee") private EmployeeTask task; } у нас нет в таблице employees к которой привязана entity Employee данных о нашей задаче, именно в таблице нет никакой task_id поэтому нам нужно Hibernate-у явно указать где прописана наша связь, а прописана она вот здесь -> class EmployeeTask { @OneToOne @JoinColumn(name = "employee_id") public Employee employee; } в классе EmployeeTask аннотацией @JoinColumn
Михаил Шапошников Уровень 1 Expert
10 января 2024
Аннотация mappedBy в @OneToOne означает, что связь уже описана в другом классе. То есть, если у вас есть два класса, Employee и EmployeeTask, и в EmployeeTask уже есть поле employee с @OneToOne, то в Employee вы говорите Hibernate: "Эй, связь у нас уже есть в EmployeeTask, просто свяжи их вместе, ты не должен создавать новый столбец для этого в Employee".