9

I have an entity that has been previously persited and has a @OneToMany relationship with another entity. In order to add a new entity I just add my new entity in the managed object and use cascadeType.ALL to persist the changes. Is there a way that I can get the id's of the newly created objects or get the original (unmanaged) object I used with the merge to have its id updated?

In pseudo-code I would expect the following to happen:

  1. New copy is going to be returned for merged entity
  2. Old copy is going to be update for new entities

Example: Parent A, id=13 Child B, id=0

In essence, I want to issue a merge on the parent but cascade persist on child (so that the original child instance is updated, not copied).

Obviously this doesn't happen. I am using hibernate as the provider.

Richard Sitze
  • 8,262
  • 3
  • 36
  • 48
Ioannis Deligiannis
  • 2,679
  • 5
  • 25
  • 48
  • What database, JPA provider and id generation strategy are you using? With Oracle 11g + Hibernate + `@SequenceGenerator` the objects cascades just fine and returns with the new assigned id. – Anthony Accioly Jul 23 '13 at 16:58
  • I am using MySql and `@GeneratedValue`. I am not a fan of pooled id's which I think is the reason why it works for Oracle/`SequenceGenerator` – Ioannis Deligiannis Jul 23 '13 at 17:11
  • @johnd I made edits to the question in an attempt to clarify; if I misunderstood please back my changes out. – Richard Sitze Jul 23 '13 at 17:34
  • Thanks for the update. I found another workaround which is to load the Parent, add my new objects and `persist` instead of merge. This the best solution so far, but being able to use merge and only cascade `persist` to new entities would be much better. – Ioannis Deligiannis Jul 23 '13 at 17:38

4 Answers4

9

Stackoverflow post and JPA documentation have the answer provided that you do your research.

The way to do what I want is to use persist on the managed parent. This will ignore any changes on the parent, but will cascade persist (provided that it is set up to cascade). The child object will have the correct id afterwards.

....
JPAEntity newObject=new JPAEntity();
managedObject.addChild(newObject);
em.persist(managedObject)
newObject.getId() //work fine!
Community
  • 1
  • 1
Ioannis Deligiannis
  • 2,679
  • 5
  • 25
  • 48
1

You should be able to "see" a generated ID for a new Entity:

  • after transaction commits, or

  • after a em.flush() (where em is your EntityManager) while a transaction is active.

Note also that all relationships between Entities need to be resolved in the Java data structures prior persistence. Child references to parents need to be "set", and vice-versa.

Richard Sitze
  • 8,262
  • 3
  • 36
  • 48
  • If I loop through the returned (merged) entity, I can see that the id has been set, no problem. But I can not see an id on the original (child/not persisted object). – Ioannis Deligiannis Jul 23 '13 at 17:06
  • 3
    The merge operation creates a new *managed copy* of the object, the entity manager does not track, change, or otherwise "manage" the original unmanaged copy. As I understand your question, you're out-of-luck. – Richard Sitze Jul 23 '13 at 17:26
1

Make sure that you are setting both sites of the relationship before persisting the changes.

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
parent.getId(); // works
parent.getChildren().get(0).getId(); // should also work
Anthony Accioly
  • 21,918
  • 9
  • 70
  • 118
  • I am setting both sides of the relationship and yes, the above works. But I don't want to traverse the collection to find the new id (I am atm, but I don't like it). There are other workarounds as well, but I would expect a more direct solution. – Ioannis Deligiannis Jul 23 '13 at 17:18
  • jonh, this is not what you've originaly asked. If you want to work with the original object, use `persist` on parent, flush and refresh. http://stackoverflow.com/questions/4870863/why-jpa-persist-does-not-generated-auto-increment-primary-id – Anthony Accioly Jul 23 '13 at 17:23
  • @AnthonyAccioly This is the correct answer. The approach shown within thw accepted one inserts duplicate parent record. – helvete Aug 15 '21 at 18:33
1

I had a similar problem with persistence provider eclipselink. I needed to add new orders from a client database with temporary "client orderIDs" to an existing customer in a server database. Because I needed the "server orderIDs" for further client requests, i wanted to create a map (clientID, serverID) and send it back to the client. Basically my approach is to get a reference to the copy of the newly added child order. It will always be added to the end of my list in parent class customer, that fact is used to retrieve it.

I use the entity classes Customer and Order, where Customer has a List (LinkedList) of orders.

Parent Customer class: ...

    @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 class:

    @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 updates customer with EntityManager.merge but I forgot to update the reference in the servlet:

CustomerService class:

    public void update(Customer entity) {
      entityManager.merge(entity);
    }  

I solved it this way: CustomerService class:

    public Customer update(Customer entity) {
      return entityManager.merge(entity);
    }

Servlet: ....

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

Now everything works fine.

Felix
  • 61
  • 1
  • 3