I have the following question. I have 3 entities and I'm using OneToOne unidirectional:
Entity1
@Entity public class Entity1 implements Serializable{ @Id @GeneratedValue(strategy= GenerationType.AUTO) Long id; String name; String value; }
Entity2
@Entity public class Entity2 implements Serializable { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @OneToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST}) Entity1 entity1; public Entity1 getEntity1() { return entity1; } String name; String value; }
Entity3
@Entity public class Entity3 implements Serializable { @Id @GeneratedValue(strategy=GenerationType.AUTO) Long id; @OneToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST}) private Entity1 entity1; public Entity1 getEntity1() { return entity1; } public void setEntity1(Entity1 entity1) { this.entity1 = entity1; } String name; String value; }
A small test:
public void testApp() { EntityManager em = TestHibernateUtil.getEntityManager(); em.getTransaction().begin(); Entity1 entity1 = new Entity1(); entity1.name = "Name1"; entity1.value = "Value1"; Entity2 entity2 = new Entity2(); entity2.name = "Name2"; entity2.value = "Value2"; entity2.setEntity1(entity1); **em.merge(entity2);**// if change that to persist - I get one Entity1 Entity3 entity3 = new Entity3(); entity3.name = "Name3"; entity3.value = "Value3"; entity3.setEntity1(entity1); **em.merge(entity3);** // if change that to persist - I get one Entity1 em.getTransaction().commit(); }
So looking into the test above, if I use em.merge
I do get 2 entities of Entity1 in persistence context after transaction commit, if I change it to em.persist
then I get one entity of Entity1 in persistence context. Can anybody explain me why that happens or point to some documentation?
Persist should be called only on new entities, while merge is meant to reattach detached entities.
The meaning of CascadeType. ALL is that the persistence will propagate (cascade) all EntityManager operations ( PERSIST, REMOVE, REFRESH, MERGE, DETACH ) to the relating entities. It seems in your case to be a bad idea, as removing an Address would lead to removing the related User .
MERGE : cascade type merge means that related entities are merged when the owning entity is merged.
CascadeType. PERSIST : It means that the save() and persist() operations in the hibernate cascade to the related entities. CascadeType. MERGE : It means that the related entities are joined when the owning entity is joined.
The behavior you are seeing is the result of two things:
em.merge(x)
will not make x
a managed object, but calling em.persist(x)
will)em.merge()
rundown:em.merge(entity2);
is invoked entity1
entity1
is copied into a new managed instance, but is not managed itself em.merge(entity3);
is invoked entity1
againentity1
is still unmanaged and doesn't have an identifier, it cannot be matched to the existing managed instance created by the previous merge. The result is that another new instance is createdentity1
exist. Two managed instances created by the merge operations and the initial unmanaged instanceNote that if your entity had an explicit id, then the second merge would not have created a new instance, but instead would have copied entity1
into the managed instance that already existed. Also, if you instead tried to merge the already managed instance, then the second merge operation would have been ignored.
em.persist()
rundown:em.persist(entity2);
is invoked entity1
entity1
is now a managed object em.persist(entity3);
is invoked entity1
againentity1
is already managed, the persist operation is ignoredentity1
exists and it is managed.entity1
is saved in the databaseThis behavior is defined in the JPA 2.0 Specification section 3.2.7.1 Merging Detached Entity State:
The semantics of the merge operation applied to an entity X are as follows:
- If X is a new entity instance, a new managed entity instance X' is created and the state of X is copied into the new managed entity instance X'.
- For all entities Y referenced by relationships from X having the cascade element value cascade=MERGE or cascade=ALL, Y is merged recursively as Y'. For all such Y referenced by X, X' is set to reference Y'. (Note that if X is managed then X is the same object as X'.)
- [...]
and section 3.2.2 Persisting and Entity Instance:
The semantics of the persist operation, applied to an entity X are as follows:
- If X is a new entity, it becomes managed. The entity X will be entered into the database at or before transaction commit or as a result of the flush operation.
- If X is a preexisting managed entity, it is ignored by the persist operation. [...]
- [...]
See also: When does the JPA set a @GeneratedValue @Id)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With