JPA Как я могу получить сгенерированный идентификатор / объект при использовании слияния из родительского объекта, но дочерний элемент создан?
У меня есть объект, который был ранее сохранен и имеет @OneToMany
отношения с другим лицом. Чтобы добавить новую сущность, я просто добавляю свою новую сущность в управляемый объект и использую cascadeType.ALL
чтобы сохранить изменения. Есть ли способ, которым я могу получить идентификаторы вновь созданных объектов или получить исходный (неуправляемый) объект, который я использовал при слиянии, чтобы обновить его идентификатор?
В псевдокоде я ожидаю, что произойдет следующее:
- Новая копия будет возвращена для объединенного объекта
- Старая копия будет обновлена для новых объектов
Пример: родитель A, id=13, ребенок B, id=0
По сути, я хочу выпустить merge
на родительском, но каскадном persist
на дочернем (так что исходный дочерний экземпляр обновляется, а не копируется).
Очевидно, этого не происходит. Я использую Hibernate в качестве поставщика.
4 ответа
Пост Stackru и документация JPA содержат ответ при условии, что вы проведете свое исследование.
Способ сделать то, что я хочу, это использовать persist
на управляемого родителя. Это будет игнорировать любые изменения в родительском, но будет каскад persist
(при условии, что он настроен на каскад). Дочерний объект впоследствии будет иметь правильный идентификатор.
....
JPAEntity newObject=new JPAEntity();
managedObject.addChild(newObject);
em.persist(managedObject)
newObject.getId() //work fine!
Вы должны быть в состоянии "увидеть" сгенерированный идентификатор для новой сущности:
после совершения транзакции, или
после
em.flush()
(гдеem
твойEntityManager
) пока транзакция активна.
Также обратите внимание, что все отношения между сущностями должны быть разрешены в структурах данных Java до сохранения. Детские ссылки на родителей должны быть "установлены", и наоборот.
У меня была похожая проблема с постоянством поставщика eclipselink. Мне нужно было добавить новые заказы из клиентской базы данных с временными "клиентскими идентификаторами заказов" существующему клиенту в серверной базе данных. Поскольку для дальнейших запросов клиентов мне нужны были "идентификаторы серверов", я хотел создать карту (clientID, serverID) и отправить ее обратно клиенту. В основном мой подход заключается в том, чтобы получить ссылку на копию недавно добавленного дочернего заказа. Он всегда будет добавлен в конец моего списка в родительском классе customer, этот факт используется для его получения.
Я использую классы сущностей Customer и Order, где у Customer есть список заказов (LinkedList).
Класс родительского клиента: ...
@OneToMany(mappedBy = "customer", cascade=CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new LinkedList<Order>();
public Order getLastOrder() {
return orders.get(orders.size()-1);
}
Детский заказ класс:
@ManyToOne(optional=false)
private Customer customer;
Servlet:
Customer customer = customerService.findByID(customerID); //existing customer
Order order = new Order();
//add attributes to new order
customer.addOrder(order);
customerService.update(customer); //generates keys for children
order = customer.getLastOrder();
Long orderID = order.getID; //Nullpointer exception
CustomerService обновляет клиента с помощью EntityManager.merge, но я забыл обновить ссылку в сервлете:
Класс CustomerService:
public void update(Customer entity) {
entityManager.merge(entity);
}
Я решил это так: класс CustomerService:
public Customer update(Customer entity) {
return entityManager.merge(entity);
}
Сервлет: ....
customer = customerService.update(customer); //generates keys for children
Теперь все работает отлично.
Убедитесь, что вы устанавливаете оба сайта отношений, прежде чем сохранять изменения.
child.setParent(parent);
parent.getChildren().add(child);
Parent parentWithId = em.merge(parent);
em.flush(); // make sure that the persistence context and database are in sync
parentWithId.getId(); // works
parentWithId.getChildren().get(0).getId(); // should also work