JPA EntityManager : Pourquoi utiliser persist() plutôt que merge() ?

EntityManager.merge() peut insérer de nouveaux objets et mettre à jour les objets existants.

Pourquoi voudrait-on utiliser persist() (qui ne peut que créer de nouveaux objets) ?

Solution

L'une ou l'autre de ces méthodes permet d'ajouter une entité à un PersistenceContext. La différence réside dans ce que vous faites ensuite de l'entité.

Persist prend une instance d'entité, l'ajoute au contexte et rend cette instance gérée (c'est-à-dire que les futures mises à jour de l'entité seront suivies).

Merge crée une nouvelle instance de votre entité, copie l'état de l'entité fournie, et rend la nouvelle copie gérée. L'instance que vous transmettez ne sera pas gérée (les modifications que vous apportez ne feront pas partie de la transaction - à moins que vous n'appeliez à nouveau merge).

Un exemple de code pourrait peut-être vous aider.

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

Les scénarios 1 et 3 sont à peu près équivalents, mais il y a certaines situations où vous voudriez utiliser le scénario 2.

Commentaires (12)

J'ai remarqué que lorsque j'utilisais em.merge, j'obtenais une instruction SELECT pour chaque INSERT, même s'il n'y avait aucun champ que JPA générait pour moi - le champ de la clé primaire était un UUID que j'avais moi-même défini. Je suis passé à em.persist(myEntityObject) et je n'ai eu que des instructions INSERT.

Commentaires (2)

Je recevais des exceptions lazyLoading sur mon entité parce que j'essayais d'accéder à une collection chargée paresseusement qui était en session.

Dans une requête séparée, je récupérais l'entité dans la session, puis j'essayais d'accéder à une collection dans ma page jsp, ce qui posait problème.

Pour pallier à ce problème, j'ai mis à jour la même entité dans mon contrôleur et je l'ai passée à mon jsp, bien que j'imagine que lorsque j'ai réenregistré en session, elle sera également accessible via SessionScope et ne lancera pas une LazyLoadingException, une modification de l'exemple 2 :

Ce qui suit a fonctionné pour moi :

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!
Commentaires (0)