Я пытался смоделировать довольно простые родительско-дочерние отношения с JPA / Hibernate, и, несмотря на большое количество уроков и публикаций по этому вопросу, я не смог заставить их работать.
У меня есть объект Customer с числовым идентификатором, в котором есть список объектов Address. Сущность Address имеет составной ключ, состоящий из внешнего ключа идентификатора клиента и порядкового номера, чтобы сделать его уникальным (но только в контексте идентификатора клиента).
Я хочу иметь возможность выполнять классические операции CRUD над объектом «Клиент», что должно быть отражено и для объектов «Адрес» (когда я добавляю, обновляю или удаляю адрес в списке клиента).
@Entity(name = "Customer ")
@Table(name = "customer ")
@DynamicUpdate
public class Customer implements Serializable
{
@Id
@Column(name = "id", nullable = false)
private Long id;
@OneToMany(mappedBy = "customer", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Address> addresses;
@Override public boolean equals(Object obj)
{
return obj instanceof Customer && ((Customer ) obj).getId() == this.getId();
}
}
-
@Entity(name = "Address")
@Table(name = "address")
@DynamicUpdate
public class Address implements Serializable
{
@Id
@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
@Id
@Column(name = "seqnumber", nullable = false)
private Integer seqnumber;
@Override public boolean equals(Object obj)
{
return obj instanceof Address
&& ((Address) obj).getCustomer().equals(this.getCustomer())
&& ((Address) obj).getSeqNumber() == this.getSeqNumber();
}
}
Создание клиента + адресов с помощью javax.persistence.EntityManager.persist и получение клиента с помощью javax.persistence.EntityManager.find работало.
Однако я не смог обновить клиента с новыми / измененными / удаленными адресами при вызове javax.persistence.EntityManager.merge.
// get data from REST service
// create customer entity - a customer with id 123 already exists in the db
Customer c = new Customer();
c.setId(123);
// create new address entity - no address with seqnumber 2 for customer 123 exists in db
Address a = new Address();
a.setCustomer(c); // or setCustomerId(123) when I tried unidirectional without the ManyToOne
a.setSeqNumber(2);
// add address to customer
c.getAddresses().add(a);
entityManager.merge(c);
Я пробовал однонаправленное отношение OneToMany со списком адресов в объекте клиента и только с идентификатором клиента с порядковым номером адреса в объекте адреса. Однако, когда я удалил адрес из списка и вместо него добавил новый, вызов слияния привел к вставке в таблицу адресов с составным ключом, равным нулю (несмотря на то, что он был установлен до слияния).
Поэтому я переключился на двунаправленное отношение OneToMany / ManyToOne и добавил объект Customer в объекте Address вместо идентификатора клиента (см. Класс выше). Это означало, что мне пришлось использовать объект customer вместо customer-id в моем методе equals в классе Address, что привело к StackOverflowError.
Может кто-нибудь дать мне подсказку, как правильно смоделировать это? Нужно ли моделировать отношения по-другому? Вызывает ли слияние неправильный путь?
Любая помощь приветствуется, спасибо заранее!
UPDATE:
Я наконец-то нашел ответ на форуме Hibernate здесь . Кажется, что функция слияния не может (всегда) гарантировать поведение, которое я ищу. Слияние должно использоваться для «повторного присоединения отсоединенных объектов, а не для получения произвольных переходных объектов».
Поэтому вместо слияния я просто получил существующую сущность через find, а затем обновил список адресов извлеченной существующей сущности, чтобы он соответствовал моей временной обновленной сущности.