JPA Как я могу получить сгенерированный идентификатор / объект при использовании слияния из родительского объекта, но дочерний элемент создан?

У меня есть объект, который был ранее сохранен и имеет @OneToMany отношения с другим лицом. Чтобы добавить новую сущность, я просто добавляю свою новую сущность в управляемый объект и использую cascadeType.ALL чтобы сохранить изменения. Есть ли способ, которым я могу получить идентификаторы вновь созданных объектов или получить исходный (неуправляемый) объект, который я использовал при слиянии, чтобы обновить его идентификатор?

В псевдокоде я ожидаю, что произойдет следующее:

  1. Новая копия будет возвращена для объединенного объекта
  2. Старая копия будет обновлена ​​для новых объектов

Пример: родитель 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
Другие вопросы по тегам