8

I'm using the JPA EntityManager to do some operations, and one such operation is replacing an entity with another entity that has the same @Id. So, given an oldObject and a newObject, what's the best way to remove the oldObject and replace it with the newObject?

Here's some code that doesn't work:

try
{
    entityManager.getTransaction().begin();
    entityManager.remove(oldObject);
    entityManager.persist(newObject);
    entityManager.getTransaction().commit();
}
catch (PersistenceException persistExc)
{
    entityManager.getTransaction().rollback();
    // do some stuff
}

This code gets the following exception:

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.depressio.SomeObject
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1215)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1148)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1154)
    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:678)

Now, of course it works OK if instead of removing the entity, I just update its fields that have changed. However, I'm just being given the new object (the UI is tossing it at me via REST), so I really don't have any clue what fields have changed, so I'd have to update every field. If there's a ton of fields, that's not ideal.

I think I'm a little confused as to how to actually do a delete properly. I thought EntityManager.remove was the right way, but I guess not.

Thanks in advance!

Edit 1: I feel I should note that oldObject.id == newObject.id. The id is generated through a @SequenceGenerator. Am I allowed to persist objects with identifiers if their identifier is annotated with a generator already?

Edit 2: According to the top upvoted answer here, because the @Id is already defined and it's an auto-generated field, Hibernate is deeming it detached. Now I know why I get the exception, but the solution is still unclear.

Community
  • 1
  • 1
Depressio
  • 1,329
  • 2
  • 20
  • 39

1 Answers1

7

That's what EntityManager.merge() does. Pass it the new, detached entity, and it will merge its state to the old, attached entity. The detached entity will stay detached, and the old one will stay attached, but have its state replaced by the state of the new, detached one.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Oh god, that's embarrassing. For some reason, I assumed `merge` only worked with attached entities, not detached ones. I guess it can work for both, huh? Anyway, that resolves the issue... so silly of me. Thanks! – Depressio May 31 '13 at 22:16
  • merging an attached entity doesn't do anything, except cascading the merge to associations annotated with cascade MERGE or ALL. – JB Nizet May 31 '13 at 22:19
  • What if I modify the attached entity? Do I even have to merge since it's attached? I get the impression that no, I don't. – Depressio May 31 '13 at 22:23
  • 1
    No. An attached entity's state is automatically made persistent by JPA. So if you do `attached = em.merge(detached); attached.setFoo("hello");`, the database row will have the state of `detached`, except for the column foo, wich will have the value "hello". – JB Nizet May 31 '13 at 22:26
  • Nice. Thanks for the tips! Still learning the magic behind JPA and trying to break my brain from its entrenched position of running native Oracle SQL scripts. :P – Depressio May 31 '13 at 22:54