Question JPA EntityManager: Pourquoi utiliser persist () sur merge ()?


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

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


839
2017-07-01 16:03


origine


Réponses:


De toute façon, ajouter une entité à un PersistenceContext, la différence est dans ce que vous faites avec l'entité par la suite.

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

Fusionner crée une nouvelle instance de votre entité, copie l'état de l'entité fournie et gère la nouvelle copie. L'instance que vous transmettez ne sera pas gérée (les modifications que vous effectuez ne feront pas partie de la transaction, à moins que vous n'appeliez à nouveau la fusion).

Peut-être qu'un exemple de code va t'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 des cas où vous voudriez utiliser le scénario 2.


1455
2017-07-01 18:28



Persister et fusionner sont à deux fins différentes (ce ne sont pas des alternatives du tout).

(édité pour développer les informations sur les différences)

persister:

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

fusionner:

  • Trouvez un objet attaché avec le même identifiant et mettez-le à jour.
  • S'il existe, mettez à jour et renvoyez l'objet déjà attaché.
  • Si n'existe pas, insérez le nouveau registre dans la base de données.

persist () efficacité:

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

persist () sémantique:

  • 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 tout registre dans le gestionnaire d'entités.

merge () pour une entité avec un identifiant 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 si connecté à MySQL merge () peut ê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 cela sera le cas partout.


151
2018-06-11 11:04



Si vous utilisez le générateur assigné, l'utilisation de la fusion au lieu de persister peut entraîner une instruction SQL redondante, affectant ainsi la performance.

Aussi, Appel de la fusion pour les entités gérées est également une erreur car les entités gérées sont automatiquement gérées par Hibernate et leur état est synchronisé avec l'enregistrement mécanisme de vérification sale sur rinçage du contexte de persistance.

Pour comprendre comment tout cela fonctionne, vous devez d'abord savoir qu'Hibernate fait passer l'état d'esprit du développeur des instructions SQL à transitions d'état d'entité.

Une fois qu'une entité est activement gérée par Hibernate, toutes les modifications seront automatiquement propagées à la base de données.

Hibernate surveille les entités actuellement attachées. Mais pour qu'une entité soit gérée, elle doit être dans le bon état d'entité.

Premièrement, nous devons définir tous les états d'entité:

  • Nouveau (Transitoire)

    Un objet nouvellement créé qui n'a jamais été associé à un Hibernate Session (alias Persistence Context) et n'est mappé à aucune ligne de table de base de données est considérée comme étant dans l'état Nouveau (transitoire).

    Pour devenir persisté, nous devons soit explicitement appeler le EntityManager#persist méthode ou faire usage du mécanisme de persistance transitive.

  • Persistant (Géré)

    Une entité persistante a été associée à une ligne de table de base de données et elle est gérée par le contexte de persistance en cours d'exécution. Toute modification apportée à une telle entité va être détectée et propagée dans la base de données (pendant le flush de la session). Avec Hibernate, nous n'avons plus besoin d'exécuter les instructions INSERT / UPDATE / DELETE. Hibernate emploie un écriture transactionnelle le style de travail et les changements sont synchronisés au dernier moment responsable, au cours du Session temps de rinçage.

  • Détaché

    Une fois que le contexte Persistence en cours est fermé, toutes les entités précédemment gérées se détachent. Les changements successifs ne seront plus suivis et aucune synchronisation automatique de la base de données ne se produira.

    Pour associer une entité détachée à une session Hibernate active, vous pouvez choisir l'une des options suivantes:

    • Rattacher

      Hibernate (mais pas JPA 2.1) prend en charge le rattachement via la méthode de mise à jour Session #. Une session Hibernate peut uniquement associer un objet Entity à une ligne de base de données donnée. En effet, le contexte de persistance agit comme un cache en mémoire (cache de premier niveau) et une seule valeur (entité) est associée à une clé donnée (type d'entité et identificateur de base de données). Une entité peut être rattachée uniquement s'il n'y a aucun autre objet JVM (correspondant à la même ligne de base de données) déjà associé à la session Hibernate en cours.

    • Fusionner

    La fusion va copier l'état de l'entité détachée (source) vers une instance d'entité gérée (destination). Si l'entité fusionnée n'a pas d'équivalent dans la session en cours, une sera extraite de la base de données. L'instance d'objet détachée continuera à rester détachée même après l'opération de fusion.

  • Supprimé

    Bien que JPA exige que seules les entités gérées puissent être supprimées, Hibernate peut également supprimer des entités détachées (mais uniquement via un appel de méthode Session # delete). Une entité supprimée est seulement programmée pour la suppression et l'instruction DELETE de base de données réelle sera exécutée pendant le flush de session.

Pour mieux comprendre les transitions d'état JPA, vous pouvez visualiser le diagramme suivant:

enter image description here

Ou si vous utilisez l'API spécifique à Hibernate:

enter image description here


108
2018-05-11 13:00



J'ai remarqué que quand j'ai utilisé em.merge, J'ai un SELECT déclaration pour chaque INSERT, même quand il n'y avait pas de champ que JPA générait pour moi - le champ clé primaire était un UUID que je me suis fixé. Je suis passé à em.persist(myEntityObject) et a juste INSERT déclarations alors.


37
2018-01-18 21:14



La spécification JPA dit ce qui suit à propos de persist().

Si X est un objet détaché, le EntityExistsException peut être jeté quand la persistance   opération est invoquée ou le EntityExistsExceptionou un autre PersistenceException peut être lancé au flush ou au temps de commit.

Donc en utilisant persist() serait approprié lorsque l'objet ne doit pas être un objet détaché. Vous préférerez peut-être que le code lance le PersistenceException donc ça échoue vite.

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


27
2018-03-11 17:23



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

Le renvoi d'une instance gérée autre que l'entité d'origine est une partie critique de la fusion   processus. Si une instance d'entité avec le même identificateur existe déjà dans le contexte de persistance,   le fournisseur remplacera son état par l'état de l'entité fusionnée, mais le   La version qui existait déjà doit être retournée au client afin qu'elle puisse être utilisée. Si le fournisseur n'a pas   mettre à jour l'instance Employee dans le contexte de persistance, toutes les références à cette instance deviendront   incompatible avec le nouvel état fusionné.

Lorsque merge () est invoqué sur une nouvelle entité, il se comporte de la même manière que l'opération persist (). Cela ajoute   l'entité au contexte de persistance, mais au lieu d'ajouter l'instance d'entité d'origine, il crée un nouveau   copier et gérer cette instance à la place. La copie créée par l'opération merge () est conservée   comme si la méthode persist () était invoquée dessus.

En présence de relations, l'opération merge () tentera de mettre à jour l'entité gérée   pour pointer vers des 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   indéfini. Certains fournisseurs peuvent autoriser la copie managée à pointer vers l'objet non persistant,   tandis que d'autres peuvent lancer une exception immédiatement. L'opération merge () peut être optionnelle   cascade dans ces cas pour empêcher une exception. Nous couvrirons la cascade de la fusion ()   opération plus tard dans cette section. Si une entité fusionnée pointe vers une entité supprimée, un   L'exception IllegalArgumentException sera levée.

Les relations de chargement paresseux sont un cas particulier dans l'opération de fusion. Si un chargement paresseux   relation n'a pas été déclenchée sur une entité avant qu'elle ne soit détachée, cette relation sera   ignoré lorsque l'entité est fusionnée. Si la relation a été déclenchée alors qu'elle était gérée, puis définie sur null alors que l'entité était détachée, la version gérée de l'entité aura également la relation effacée pendant la fusion. "

Toutes les informations ci-dessus proviennent de "Pro JPA 2 Mastering Java Persistence API" par Mike Keith et Merrick Schnicariol. Chapitre 6. Détachement de section et fusion. Ce livre est en fait un deuxième livre consacré à JPA par les auteurs. Ce nouveau livre contient de nombreuses nouvelles informations puis une ancienne. J'ai vraiment recommandé de lire ce livre pour ceux qui seront sérieusement impliqués avec JPA. Je suis désolé de poster ma première réponse anonymement.


16
2017-10-05 13:00



Il y a plus de différences entre merge et persist (Je vais énumérer à nouveau ceux déjà postés ici):

D1. merge ne rend pas l'entité transmise gérée, mais renvoie plutôt une autre instance gérée. persist de l'autre côté rendra l'entité passée géré:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2. Si vous supprimez une entité et décidez ensuite de conserver l'entité, vous pouvez le faire uniquement avec persist (), car merge jettera un IllegalArgumentException.

D3. Si vous avez décidé de prendre soin manuellement de vos ID (par exemple, en utilisant des UUID), un merge  l'opération déclenchera ultérieurement SELECT requêtes afin de rechercher des entités existantes avec cet ID, tout en persist ne peut pas avoir besoin de ces requêtes.

D4. Il y a des cas où vous n'avez simplement pas confiance au code qui appelle votre code, et afin de vous assurer qu'aucune donnée n'est mise à jour, mais plutôt insérée, vous devez utiliser persist.


15
2017-12-02 14:13