Hibernate связь OneToOne, сторона-владелец не сохраняет внешний ключ

Вопрос или проблема

Я изучаю Hibernate (v. 6.6.1 финальная версия) и у меня проблема с отношением OneToOne

  1. Сущность Customer
@Entity
@Table(name = "customer")
public class Customer {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "name")
    private String name;
    @Column(name = "age")
    private int age;

    @OneToOne(mappedBy = "customer", cascade = CascadeType.PERSIST)
    private Passport passport;
    // конструкторы, геттеры и сеттеры
}
  1. Сущность Passport
@Entity
@Table(name = "passport")
public class Passport {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "passport_number")
    private String passportNumber;

    @OneToOne
    @JoinColumn(name = "customer_id", referencedColumnName = "id")
    private Customer customer;
    // конструкторы, геттеры и сеттеры
}
  1. Я настраиваю отношение OneToOne
try {
            
   session.beginTransaction();

   Customer customer = new Customer("Customer 1", 77);
   Passport passport = new Passport("777");

   customer.setPassport(passport);
   session.persist(customer);

   session.getTransaction().commit();
}
finally {
   factory.close();
}

И после этого в базе данных сохраняется:

Customer {id: 1, name: “Customer 1”, age: 77}

и

Passport {id: 1, passport_number: “777”, customer_id: null}

Почему hibernate не сохраняет person_id, если я настроил отношение?

Но если я связываю с обеих сторон, тогда все в порядке

try {
            
   session.beginTransaction();

   Customer customer = new Customer("Customer 1", 77);
   Passport passport = new Passport("777");
   
   passport.setCustomer(customer);
   customer.setPassport(passport);

   session.persist(customer);

   session.getTransaction().commit();
}
finally {
   factory.close();
}

Мой вопрос: почему я должен вручную устанавливать отношение для каждого объекта, если я уже настроил отношение? Почему hibernate не сохраняет customer_id, но сохраняет объект Passport?

Попробуйте связать отношение с обеих сторон, это работает, но почему hibernate автоматически не сохраняет мой внешний ключ?

Если вы хотите определить отношение один к одному, вам нужно определить ваши сущности следующим образом:

  • customer: { int id(PK), String name, int age, int passportId(FK) }
  • passport: { int id(PK), String passportNumber, int customerId(FK) }

Каждая сущность ссылается друг на друга с помощью внешнего ключа, чтобы иметь это отношение, и внешний ключ, связанный с его идентификаторами должен иметь одинаковый тип данных.

Попробуйте это…

Ответ или решение

Процесс Сохранения Внешнего Ключа в Ассоциации OneToOne с Hibernate

В этой статье мы рассмотрим проблему, с которой вы столкнулись при работе с ассоциацией OneToOne в Hibernate (версия 6.6.1), а именно: почему при сохранении объекта Customer не сохраняется внешний ключ customer_id в таблице passport.

Понимание Ассоциации OneToOne

В ассоциации OneToOne каждый объект одного типа может быть связан только с одним объектом другого типа. Например, в вашем случае каждый Customer связан с уникальным Passport, и наоборот. Однако, чтобы Hibernate смог правильно управлять этими отношениями и корректно сохранять данные, необходимо учитывать несколько моментов.

Код Сущностей

В вашем коде вы настроили ассоциацию следующим образом:

Сущность Customer:

@Entity
@Table(name = "customer")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "name")
    private String name;
    @Column(name = "age")
    private int age;

    @OneToOne(mappedBy = "customer", cascade = CascadeType.PERSIST)
    private Passport passport;

    // Конструкторы, геттеры и сеттеры
}

Сущность Passport:

@Entity
@Table(name = "passport")
public class Passport {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "passport_number")
    private String passportNumber;

    @OneToOne
    @JoinColumn(name = "customer_id", referencedColumnName = "id")
    private Customer customer;

    // Конструкторы, геттеры и сеттеры
}

Проблема

Когда вы сохраняете объект Customer и его Passport с помощью следующего кода:

customer.setPassport(passport);
session.persist(customer);

Hibernate не сохраняет customer_id в таблице passport, и вы получаете запись с customer_id равным null.

Объяснение Проблемы

Основная причина, по которой customer_id не сохраняется в таблице passport, заключается в том, что в вашей конфигурации ассоциации Address определена как владеющая сторона (owning side), но вы не установили обратную ссылку (customer.setPassport(passport);) на другой объект. Ваша первая часть кода сохраняет Customer, но в этом случае Passport не знает о своем связанном Customer и не устанавливает необходимый внешний ключ.

В Hibernate ассоциации OneToOne требуют, чтобы обе стороны (объект-владелец и объект-не владелец) были правильно связаны. Если вы не устанавливаете обе ссылки, то Hibernate просто не знает о том, что объект Passport должен ссылаться на Customer.

Решение

Чтобы решить эту проблему, вы должны явным образом установить связь с обеих сторон:

passport.setCustomer(customer);
customer.setPassport(passport);

После этого Hibernate сможет корректно обработать изменения и установить соответствующий внешний ключ в таблице passport.

Вывод

При проектировании ассоциаций в Hibernate, особенно в OneToOne, важно помнить о необходимости устанавливать связь с обеих сторон. Это дает Hibernate необходимую информацию для правильного управления объектами и их сохранения. Если вы сначала установите одну сторону, а затем не обновите другую, вы можете столкнуться с проблемами, подобными описанным в вашем случае. Это ключевой аспект работы с Hibernate, который стоит учитывать при разработке приложений на этом фреймворке.

Оцените материал
Добавить комментарий

Капча загружается...