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

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

В псевдокоде я ожидал бы следующего:

  • Новая копия будет возвращена для объединенного объекта
  • Старая копия будет обновляться для новых объектов

Пример: Родитель A, id = 13 Ребенок B, id = 0

В сущности, я хочу выпустить merge на родительском, но каскад persist на дочерний элемент (так что исходный дочерний экземпляр обновляется, а не копируется).

Очевидно, этого не происходит. Я использую hibernate в качестве поставщика.

Ответ 1

Stackoverflow post и Документация JPA имеют ответ, если вы делаете свой исследование.

Способ сделать то, что я хочу, это использовать persist для управляемого родителя. Это игнорирует любые изменения родителя, но будет каскадно persist (при условии, что он настроен на каскад). После этого дочерний объект будет иметь правильный идентификатор.

....
JPAEntity newObject=new JPAEntity();
managedObject.addChild(newObject);
em.persist(managedObject)
newObject.getId() //work fine!

Ответ 2

Вы должны иметь возможность "видеть" сгенерированный идентификатор для нового объекта:

  • после совершения транзакции или

  • после em.flush() (где em - ваш EntityManager), когда транзакция активна.

Обратите внимание также, что все отношения между объектами должны быть разрешены в структурах данных Java ранее. Обращения детей к родителям должны быть "установлены" и наоборот.

Ответ 3

У меня была аналогичная проблема с провайдером обеспечения непрерывности eclipselink. Мне нужно было добавить новые заказы из клиентской базы данных с временными "идентификаторами клиента" к существующему клиенту в базе данных сервера. Поскольку мне нужны "server orderID" для последующих клиентских запросов, я хотел создать карту (clientID, serverID) и отправить ее клиенту. В основном мой подход заключается в том, чтобы получить ссылку на копию недавно добавленного дочернего заказа. Он всегда будет добавлен в конец моего списка в родительском классе, этот факт используется для его получения.

Я использую классы объектов Customer и Order, где у Клиента есть список (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);
    }

Класс Child Order:

    @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);
    }

Servlet:....

    customer = customerService.update(customer); //generates keys for children

Теперь все работает нормально.

Ответ 4

Перед сохранением изменений убедитесь, что вы устанавливаете оба узла отношений.

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