1069 votes

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) ?

14 votes

2 votes

Si vous aimez les diagrammes. Référez-vous à ceci : spitballer.blogspot.in/2010/04/

1768voto

Mike Points 9244

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 renvoie l'instance gérée avec laquelle l'état a été fusionné. Elle renvoie quelque chose qui existe dans PersistenceContext ou crée une nouvelle instance de votre entité. Dans tous les cas, elle copiera l'état de l'entité fournie, et retournera une 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). Cependant, vous pouvez utiliser l'instance retournée (une instance gérée).

Peut-être qu'un exemple de code vous aidera.

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 dans certaines situations, il est préférable d'utiliser le scénario 2.

217voto

Josep M. Panadero Points 445

Persister et fusionner ont deux objectifs différents (ce ne sont pas du tout des alternatives).

(édité pour élargir les informations sur les différences)

persistent :

  • Insérer un nouveau registre dans la base de données
  • Attachez l'objet au gestionnaire d'entités.

fusionner :

  • Trouver un objet attaché avec le même id et le mettre à jour.
  • S'il existe, mettre à jour et retourner l'objet déjà attaché.
  • S'il n'existe pas, insérer le nouveau registre dans la base de données.

persist() efficacité :

  • Il pourrait être plus efficace pour insérer un nouveau registre dans une base de données que merge().
  • Il ne duplique pas l'objet original.

sémantique persist() :

  • Il s'assure que vous insérez et ne mettez pas à jour par erreur.

Exemple :

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

De cette façon, il n'existe qu'un seul objet attaché pour chaque registre dans le gestionnaire d'entités.

merge() pour une entité avec un id est quelque chose comme :

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

Bien que, s'il était connecté à MySQL, merge() pourrait être aussi efficace que persist() en utilisant un appel à INSERT avec l'option ON DUPLICATE KEY UPDATE, JPA est une programmation de très haut niveau et vous ne pouvez pas supposer que ce sera le cas partout.

0 votes

Pouvez-vous citer un cas où il n'est pas valide de remplacer em.persist(x) con x = em.merge(x) ?

28 votes

Persist() peut lancer une EntityExistsException. Si vous voulez être sûr que votre code effectue une insertion et non une mise à jour des données, vous devez utiliser persist.

1 votes

merge() peut également lancer un EntityExistsException

38voto

Sarah Vessels Points 8195

J'ai remarqué que lorsque j'ai utilisé em.merge J'ai eu un SELECT pour chaque INSERT même si JPA ne générait aucun champ 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 j'ai juste INSERT des déclarations alors.

4 votes

C'est logique puisque vous attribuez les ID et que le conteneur JPA n'a aucune idée d'où vous les avez obtenus. Il y a une (petite) chance que l'objet existe déjà dans la base de données, par exemple dans un scénario où plusieurs applications écrivent dans la même base de données.

0 votes

J'ai rencontré le même problème avec merge() . J'avais une base de données PostgreSQL avec des voir la vue agrégeait des données provenant de plusieurs tables (les tables avaient une structure identique mais des noms différents). JPA a donc essayé de faire merge() mais en fait, JPA a d'abord fait SELECT (la base de données, en raison des paramètres de la vue, pouvait renvoyer plusieurs enregistrements avec la même clé primaire provenant de différentes tables !), puis JPA (Hibernate était une implémentation) a échoué : il y a plusieurs enregistrements avec la même clé ( org.hibernate.HibernateException: More than one row with the given identifier was found ). Dans mon cas persist() m'a aidé.

29voto

Raedwald Points 8862

La spécification JPA dit ce qui suit au sujet de persist() .

Si X est un objet détaché, le EntityExistsException peut être déclenchée lorsque la persistance est invoquée, ou l'opération EntityExistsException ou un autre PersistenceException peuvent être lancés au moment du flush ou du commit.

Donc, en utilisant persist() conviendrait lorsque l'objet ne doit pas pour être un objet détaché. Vous pourriez préférer que le code jette le PersistenceException donc il échoue rapidement.

Bien que la spécification n'est pas claire , persist() pourrait fixer le @GeneratedValue @Id pour un objet. merge() doit cependant avoir un objet avec l'attribut @Id déjà généré.

5 votes

+1 pour " merge() doit cependant avoir un objet avec l'attribut @Id déjà généré . ". Chaque fois que l'EntityManager ne trouve pas de valeur pour le champ de l'ID de l'objet, celui-ci est persisté (inséré) dans la BD.

0 votes

Je ne l'ai pas compris en premier car je n'étais pas clair sur les états. J'espère que cela aidera quelqu'un comme ce fut le cas pour moi. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/

1 votes

@GeneratedValue n'a pas d'implication différente pour merge() et persist().

18voto

Khurshed Salimov Points 126

Quelques détails supplémentaires sur la fusion qui vous aideront à utiliser la fusion plutôt que la persistance :

Le renvoi d'une instance gérée autre que l'entité d'origine est un élément essentiel de la fusion. de la fusion. Si une instance d'entité avec le même identifiant existe déjà dans le contexte de persistance, le fournisseur écrasera son état avec l'état de l'entité d'origine. contexte de persistance, le fournisseur écrasera son état avec l'état de l'entité en cours de fusion. qui existait déjà doit être renvoyée au client afin qu'il puisse l'utiliser. Si le fournisseur n'a pas mis à jour l'instance Employee dans le contexte de persistance, toute référence à cette instance deviendra incohérentes avec le nouvel état en cours de fusion.

Lorsque la fonction merge() est invoquée sur une nouvelle entité, elle se comporte de manière similaire à l'opération persist(). Elle ajoute l'entité au contexte de persistance, mais au lieu d'ajouter l'instance d'entité originale, elle crée une nouvelle copie et gère cette insta copie et gère cette instance à la place. La copie qui est créée par l'opération merge() est persistée comme si la méthode persist() avait été invoquée sur elle.

En présence de relations, l'opération merge() tentera de mettre à jour l'entité gérée. pour pointer vers les versions gérées des entités référencées par l'entité détachée. Si l'entité a un relation avec un objet qui n'a pas d'identité persistante, le résultat de l'opération de fusion est le suivant indéfini. Certains fournisseurs pourraient permettre à la copie gérée de pointer vers l'objet non persistant, tandis que d'autres peuvent immédiatement lever une exception. L'opération merge() peut être optionnellement en cascade dans ces cas pour éviter qu'une exception ne se produise. Nous aborderons la mise en cascade de l'opération merge() plus loin dans cette section. Si une entité en cours de fusion pointe vers une entité supprimée, une IllegalArgumentException sera levée.

Les relations paresseuses constituent un cas particulier dans l'opération de fusion. Si une relation de type "lazy-loading n'a pas été déclenchée sur une entité avant qu'elle ne soit détachée, cette relation sera ignorée lors de la fusion de l'entité. Si la relation a été déclenchée alors que l'entité était gérée, puis mise à zéro alors que l'entité était détachée, la version gérée de l'entité verra également la relation effacée lors de la fusion."

Toutes les informations ci-dessus ont été extraites de "Pro JPA 2 Mastering the Java™ Persistence API" de Mike Keith et Merrick Schnicariol. Chapitre 6. Détachement et fusion de sections. Ce livre est en fait un deuxième livre consacré à JPA par les auteurs. Ce nouveau livre contient beaucoup de nouvelles informations par rapport au précédent. Je recommande vraiment de lire ce livre pour ceux qui seront sérieusement impliqués dans JPA. Je suis désolé d'avoir posté ma première réponse de manière anonyme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X