How Does Merge Work in JPA (and Hibernate)
The JPA EntityManager
there are the merge()
and persist()
methods. In hibernate there’s more – save
, persist
, saveOrUpdate
, update, merge. I’m always using merge()
for the sake of simplicity, but one must be aware what is the exact behaviour of this method.
I had to read the JPA spec (1.0, but I guess it should be unchanged in 2.0) in order to get the following:
- if the entity is already in the persistence context (session), no action is taken, except for cascades
- if the entity is detached, a copy (object’) is returned, which is attached (managed)
- if the entity is transient (new instance), it is saved and a persistent (and managed) copy is returned
- if the entity is detached, but an object with the same identifier exist in the current entity manager, then the state of the detached object is copied into the current persistent entity, and it (the current) is returned
Here’s a test that depicts what’s happening:
Message msg = new Message();
msg.setText("Foo");
Message msg2 = em.merge(msg);
System.out.println("New, transient object: " + msg);
System.out.println("Merged object: " + msg2);
Message msg3 = em.merge(msg2);
System.out.println("Merging persistent object: " + msg3);
Message msg4 = new Message();
msg4.setText("Bar");
msg4.setId(msg3.getId());
Message msg5 = em.merge(msg4);
System.out.println("Detached entity with same id: " + msg5);
System.out.println("Merged text: " + msg5.getText());
em.clear();
Message msg6 = em.merge(msg5);
System.out.println("Merged detached entity: " + msg6);
And the resulet is:
New, transient object: com.foo.model.Message@1f
Merged object: com.foo.model.Message@8da6fe83
Merging persistent object: com.foo.model.Message@8da6fe83
Detached entity with same id: com.foo.model.Message@8da6fe83
Merged text: Bar
Hibernate: select message0_.id as id1_1_, message0_.authorId .....
Merged detached entity: com.foo.model.Message@a13fade8
I haven’t had problems with using merge()
only, but one must know what he’s getting.
(Note: The same goes for Hibernate’s merge()
)
Update: you can read more on the topic here
The JPA EntityManager
there are the merge()
and persist()
methods. In hibernate there’s more – save
, persist
, saveOrUpdate
, update, merge. I’m always using merge()
for the sake of simplicity, but one must be aware what is the exact behaviour of this method.
I had to read the JPA spec (1.0, but I guess it should be unchanged in 2.0) in order to get the following:
- if the entity is already in the persistence context (session), no action is taken, except for cascades
- if the entity is detached, a copy (object’) is returned, which is attached (managed)
- if the entity is transient (new instance), it is saved and a persistent (and managed) copy is returned
- if the entity is detached, but an object with the same identifier exist in the current entity manager, then the state of the detached object is copied into the current persistent entity, and it (the current) is returned
Here’s a test that depicts what’s happening:
Message msg = new Message(); msg.setText("Foo"); Message msg2 = em.merge(msg); System.out.println("New, transient object: " + msg); System.out.println("Merged object: " + msg2); Message msg3 = em.merge(msg2); System.out.println("Merging persistent object: " + msg3); Message msg4 = new Message(); msg4.setText("Bar"); msg4.setId(msg3.getId()); Message msg5 = em.merge(msg4); System.out.println("Detached entity with same id: " + msg5); System.out.println("Merged text: " + msg5.getText()); em.clear(); Message msg6 = em.merge(msg5); System.out.println("Merged detached entity: " + msg6);
And the resulet is:
New, transient object: com.foo.model.Message@1f Merged object: com.foo.model.Message@8da6fe83 Merging persistent object: com.foo.model.Message@8da6fe83 Detached entity with same id: com.foo.model.Message@8da6fe83 Merged text: Bar Hibernate: select message0_.id as id1_1_, message0_.authorId ..... Merged detached entity: com.foo.model.Message@a13fade8
I haven’t had problems with using merge()
only, but one must know what he’s getting.
(Note: The same goes for Hibernate’s merge()
)
Update: you can read more on the topic here
What about the case where an entity is new (detached) and you set a random (non existing) id on it. Shoulnd’t merge fail with some sort of not found exception, since the id implies that you’re merging changes into an existing entity, and instead there is no entity with that particular id?
Thanks.
I’m not sure. If the ID is autogenerated it might fail, but otherwise it will probably insert it as new.
@Eugen: This is implementation dependent.
Hibernate uses the ID to determine whether an instance already exists in the database.
So when you set the ID to 0 or null, merge()/save() will insert a new instance (plus assign a new id). You will end with two different rows in the database.
If you set the ID, Hibernate will try to update the existing row which will fail.
Thanks, nice post
Would it be correct to say that after a `o2 = em.merge(o1)` you should always discard o1 and continue with o2?
It would seem like anything you do on o2 would happen on the database, while anything you do on o1 may or may not happen in the database depending on the specific merge-scenario.