Вопрос или проблема
Я изучаю Hibernate (v. 6.6.1 финальная версия) и у меня проблема с отношением OneToOne
- Сущность 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;
// конструкторы, геттеры и сеттеры
}
- Сущность 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;
// конструкторы, геттеры и сеттеры
}
- Я настраиваю отношение 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, который стоит учитывать при разработке приложений на этом фреймворке.