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


Réponses:


1615

Dans les deux cas, une entité sera ajoutée à un PersistenceContext, la différence réside dans ce que vous faites ensuite avec l'entité.

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

La fusion renvoie l'instance gérée vers laquelle l'état a été fusionné. Il retourne quelque chose qui existe dans PersistenceContext ou crée une nouvelle instance de votre entité. Dans tous les cas, il copiera l'état de l'entité fournie et retournera la copie gérée. L'instance que vous transmettez ne sera pas gérée (toutes les modifications que vous apporterez ne feront pas partie de la transaction - sauf si vous appelez à nouveau la fusion). Vous pouvez utiliser une instance retournée (une 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 il existe certaines situations où vous souhaitez utiliser le scénario 2.


3
@dma_k: On dirait que vous utilisez Hibernate. Je suis moins familier avec Hibernate qu'avec JPA - mais en JPA si vous appelez EntityManager.persist () et passez dans une entité détachée, vous: a) obtiendrez immédiatement une EntityExistsException ou b) obtiendrez une autre PersistenceException au moment du flush / commit. J'ai peut-être mal compris la question ici?
Mike

49
Cette réponse pourrait être améliorée si elle couvrait également les cas où l'entité fusionnée / persistante existe déjà dans le contexte persistant (ou du moins indiquait clairement qu'elle ne décrit le comportement que lorsque l'entité persistante / fusionnée n'existe pas déjà)
Henry

2
Une méthode est-elle plus performante? Peut-être que mergela copie complète d'un objet avant de le gérer a un impact sur les performances?
Kevin Meredith

2
Et les identifiants? Si j'en ai un, @GeneratedIdpuis-je l'obtenir dans le scénario 2?
rascio

7
Mike: "Merge crée une nouvelle instance ...": ce n'est pas toujours vrai. Si EntityManager trouve une entité déjà gérée dans son contexte, il renvoie cette instance (après avoir mis à jour les champs). Veuillez modifier votre réponse, puis je voterai pour.
Heri

181

La persistance et la fusion ont deux objectifs différents (ce ne sont pas du tout des alternatives).

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

persister:

  • Insérez 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é.
  • S'il n'existe pas, insérez le nouveau registre dans la base de données.

efficacité persist ():

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

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 n'importe quel 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 est connecté à MySQL merge () puisse ê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 va être le cas partout.


Pouvez - vous nommer un cas où il n'est pas valable pour remplacer em.persist(x)avec x = em.merge(x)?
Aaron Digulla

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

1
merge()peut également lancer unEntityExistsException
Sean

1
@None Cela pourrait être le cas RuntimeException, mais ce n'est pas mentionné dans le Javadoc.
Martin

154

Si vous utilisez le générateur attribué, utilisation de la fusion au lieu de la persistance peut entraîner une instruction SQL redondante , affectant ainsi les performances.

De plus, appeler 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 de la base de données par le mécanisme de vérification incorrect lors de vidage du contexte de persistance .

Pour comprendre comment tout cela fonctionne, vous devez d'abord savoir que Hibernate fait évoluer l'état d'esprit des développeurs des instructions SQL vers transitions d'état d'entité .

Une fois qu'une entité est activement gérée par Hibernate, toutes les modifications vont être 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é.

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

Transitions d'état d'entité JPA

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

Hibernation des transitions d'état d'entité

Comme illustré par les diagrammes ci-dessus, une entité peut se trouver dans l'un des quatre états suivants:

  • Nouveau (transitoire)

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

    Pour devenir persistant, nous devons soit appeler explicitement la EntityManager#persistméthode, soit utiliser le 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 à la base de données (pendant le vidage de session). Avec Hibernate, nous n'avons plus à exécuter les instructions INSERT / UPDATE / DELETE. Hibernate utilise un style de travail transactionnel à écriture différée et les modifications sont synchronisées au tout dernier moment responsable, pendant le Sessiontemps de vidage actuel .

  • Détaché

    Une fois le contexte de persistance en cours d'exécution fermé, toutes les entités précédemment gérées se détachent. Les modifications successives ne seront plus suivies et aucune synchronisation automatique de la base de données ne se produira.

    Pour associer une entité détachée à une session de mise en veille prolongée active, vous pouvez choisir l'une des options suivantes:

    • Rattachement

      Hibernate (mais pas JPA 2.1) prend en charge le rattachement via la méthode de mise à jour Session #. Une session Hibernate ne peut associer qu'un seul objet Entity pour 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 identifiant de base de données). Une entité ne peut être rattachée que si aucun autre objet JVM (correspondant à la même ligne de base de données) n'est déjà associé à la session Hibernate en cours.

    • Fusion

    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é restera 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 soient supprimées, Hibernate peut également supprimer les entités détachées (mais uniquement via un appel de méthode de suppression de session #). La suppression d'une entité supprimée est uniquement planifiée et l'instruction DELETE de la base de données réelle sera exécutée pendant le vidage de session.



@gstackoverflow La réponse que vous avez obtenue est la bonne. Pour plus de détails, consultez cet article ou mon livre, Persistance Java haute performance .
Vlad Mihalcea

Il n'y a donc pas de possibilité de changer l'ordre d'opération pour orphanremoval = true?
gstackoverflow

Votre article sur l'ordre de fonctionnement dans les cas habituels. Ma question spécifique pour orphanRemoval
gstackoverflow

1
Le fait est qu'il est impossible d'expliquer l'hibernation avec un diagramme comme celui-ci. Pourquoi ne pouvez-vous pas vider la session après le détachement? Que se passe-t-il lorsque vous essayez de sauvegarder une entité déjà persistante? Pourquoi ce comportement de flush est-il différent quand il s'agit de sauvegarder et de persister? Il y a 1000 questions de ce genre, auxquelles personne n'a de logique claire.
GingerBeer

37

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


3
Cela a du sens puisque vous attribuez les ID et que le conteneur JPA n'a aucune idée d'où vous l'avez obtenu. 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.
Aaron Digulla

J'ai rencontré un problème similaire avec merge(). J'avais une base de données PostgreSQL avec une vue compliquée : la vue agrégeait les données de plusieurs tables (les tables avaient une structure identique mais des noms différents). JPA a donc essayé de le faire merge(), mais en fait JPA a d'abord été créé SELECT(la base de données en raison des paramètres d'affichage peut renvoyer plusieurs enregistrements avec la même clé primaire 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, cela persist()m'a aidé.
flaz14

29

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

Si X est un objet détaché, le EntityExistsExceptionpeut être levé lorsque l'opération de persistance est invoquée, ou le EntityExistsExceptionou un autre PersistenceExceptionpeut être levé lors du vidage ou de la validation.

L'utilisation persist()serait donc appropriée lorsque l'objet ne devrait pas être un objet détaché. Vous préférerez peut-être que le code lance le PersistenceExceptionafin qu'il échoue rapidement.

Bien que la spécification ne soit pas claire , persist()peut définir le @GeneratedValue @Idpour un objet. merge()cependant doit avoir un objet avec le @Iddéjà généré.


5
+1 pour " merge()cependant doit avoir un objet avec le @Id déjà généré . ". Chaque fois que l'EntityManager ne trouve pas de valeur pour le champ de l'ID d'objet, il est conservé (inséré) dans la base de données.
Omar

Je n'ai pas compris cela d'abord car je n'étais pas clair sur les états. J'espère que cela aide quelqu'un comme il l'a fait pour moi. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz

1
@GeneratedValue n'a aucune implication différente pour merge () et persist ()
SandeepGodara

17

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 un élément essentiel du processus de fusion. Si une instance d'entité avec le même identifiant existe déjà dans le contexte de persistance, le fournisseur remplacera son état par l'état de l'entité qui est fusionnée, mais la version gérée qui existait déjà doit être retournée au client afin qu'elle puisse être utilisé. Si le fournisseur n'a pas mis à jour l'instance Employee dans le contexte de persistance, toute référence à cette instance deviendra incohérente 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 (). Il ajoute l'entité au contexte de persistance, mais au lieu d'ajouter l'instance d'entité d'origine, il crée une nouvelle copie et gère cette instance à la place. La copie créée par l'opération merge () est conservée comme si la méthode persist () y était invoquée.

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 une relation avec un objet qui n'a pas d'identité persistante, le résultat de l'opération de fusion n'est pas défini. Certains fournisseurs peuvent autoriser la copie gérée à pointer vers l'objet non persistant, tandis que d'autres peuvent lever immédiatement une exception. L'opération merge () peut éventuellement être mise en cascade dans ces cas pour éviter qu'une exception ne se produise. Nous couvrirons la 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 exception IllegalArgumentException sera levée.

Les relations de chargement différé sont un cas particulier dans l'opération de fusion. Si une relation de chargement différé n'a pas été déclenchée sur une entité avant de se détacher, cette relation sera ignorée lors de la fusion de l'entité. Si la relation a été déclenchée lors de sa gestion, puis définie sur null lors du détachement de l'entité, la version gérée de l'entité verra également la relation effacée lors de la fusion. "

Toutes les informations ci-dessus sont 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 de nombreuses informations nouvelles puis anciennes. J'ai vraiment recommandé de lire ce livre pour ceux qui seront sérieusement impliqués avec JPA. Je suis désolé d'avoir posté de manière anonyme ma première réponse.


17

Il y a encore plus de différences entre mergeetpersist (je vais énumérer à nouveau celles déjà publiées ici):

D1. mergene gère pas l'entité transmise, mais renvoie plutôt une autre instance gérée. persistde l'autre côté, l'entité transmise sera gérée:

//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 la conserver, vous ne pouvez le faire qu'avec persist (), car mergejettera un IllegalArgumentException.

D3. Si vous avez décidé de prendre soin manuellement de vos identifiants (par exemple en utilisant des UUID), une merge opération déclenchera des SELECTrequêtes ultérieures afin de rechercher les entités existantes avec cet identifiant, tout en persistn'ayant pas besoin de ces requêtes.

D4. Il y a des cas où vous ne faites tout 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.


8

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

Ce que je ferais, c'était dans une demande distincte, récupérer l'entité de la session, puis essayer d'accéder à une collection dans ma page jsp qui était problématique.

Pour remédier à cela, j'ai mis à jour la même entité dans mon contrôleur et l'ai passée à mon jsp, bien que j'imagine quand j'ai réenregistré en session qu'elle sera également accessible SessionScopeet ne lâchera pas 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!!

7

J'ai trouvé cette explication des documents Hibernate éclairante, car ils contiennent un cas d'utilisation:

L'utilisation et la sémantique de merge () semblent être déroutantes pour les nouveaux utilisateurs. Premièrement, tant que vous n'essayez pas d'utiliser l'état d'objet chargé dans un gestionnaire d'entités dans un autre nouveau gestionnaire d'entités, vous ne devriez pas du tout avoir besoin d'utiliser merge () . Certaines applications entières n'utiliseront jamais cette méthode.

Merge () est généralement utilisé dans le scénario suivant:

  • L'application charge un objet dans le premier gestionnaire d'entités
  • l'objet est transmis à la couche de présentation
  • quelques modifications sont apportées à l'objet
  • l'objet est retransmis à la couche logique métier
  • l'application persiste ces modifications en appelant merge () dans un deuxième gestionnaire d'entités

Voici la sémantique exacte de merge ():

  • s'il existe une instance gérée avec le même identifiant actuellement associé au contexte de persistance, copiez l'état de l'objet donné sur l'instance gérée
  • si aucune instance gérée n'est actuellement associée au contexte de persistance, essayez de la charger depuis la base de données ou créez une nouvelle instance gérée
  • l'instance gérée est renvoyée
  • l'instance donnée ne devient pas associée au contexte de persistance, elle reste détachée et est généralement supprimée

De: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html


6

En parcourant les réponses, il manque quelques détails concernant la cascade et la génération d'ID. Voir la question

En outre, il convient de mentionner que vous pouvez avoir des Cascadeannotations distinctes pour la fusion et la persistance: Cascade.MERGEetCascade.PERSIST qui seront traitées selon la méthode utilisée.

La spécification est votre amie;)


6

JPA est incontestablement une grande simplification dans le domaine des applications d'entreprise construites sur la plate-forme Java. En tant que développeur qui a dû faire face aux subtilités des anciens beans entité dans J2EE, je vois l'inclusion de JPA parmi les spécifications Java EE comme un grand pas en avant. Cependant, tout en approfondissant les détails de l'APP, je trouve des choses qui ne sont pas si faciles. Dans cet article, je traite de la comparaison des méthodes de fusion et de persistance d'EntityManager dont le comportement de chevauchement peut causer de la confusion non seulement à un débutant. De plus, je propose une généralisation qui voit les deux méthodes comme des cas particuliers d'une méthode plus générale se combinent.

Entités persistantes

Contrairement à la méthode de fusion, la méthode persist est assez simple et intuitive. Le scénario le plus courant de l'utilisation de la méthode persist peut être résumé comme suit:

"Une instance nouvellement créée de la classe d'entité est transmise à la méthode persist. Après le retour de cette méthode, l'entité est gérée et planifiée pour insertion dans la base de données. Cela peut se produire au moment de la validation de la transaction ou avant l'appel de la méthode flush. Si l'entité fait référence à une autre entité via une relation marquée avec la stratégie de cascade PERSIST, cette procédure lui est également appliquée. "

entrez la description de l'image ici

La spécification va plus dans les détails, cependant, leur souvenir n'est pas crucial car ces détails ne couvrent que des situations plus ou moins exotiques.

Fusion d'entités

En comparaison de persister, la description du comportement de la fusion n'est pas si simple. Il n'y a pas de scénario principal, comme c'est le cas pour persist, et un programmeur doit se souvenir de tous les scénarios afin d'écrire un code correct. Il me semble que les concepteurs JPA voulaient avoir une méthode dont la principale préoccupation serait de gérer les entités détachées (contrairement à la méthode persistante qui traite principalement des entités nouvellement créées.) La tâche principale de la méthode de fusion est de transférer l'état d'un entité non gérée (passée comme argument) à son homologue géré dans le contexte de persistance. Cette tâche, cependant, se divise en plusieurs scénarios qui aggravent l'intelligibilité du comportement global de la méthode.

Au lieu de répéter les paragraphes de la spécification JPA, j'ai préparé un organigramme qui décrit schématiquement le comportement de la méthode de fusion:

entrez la description de l'image ici

Alors, quand dois-je utiliser persist et quand fusionner?

persister

  • Vous voulez que la méthode crée toujours une nouvelle entité et ne mette jamais à jour une entité. Sinon, la méthode lève une exception à la suite d'une violation d'unicité de la clé primaire.
  • Processus par lots, gérant les entités de manière dynamique (voir Modèle de passerelle).
  • Optimisation des performances

fusionner

  • Vous voulez que la méthode insère ou mette à jour une entité dans la base de données.
  • Vous souhaitez gérer les entités sans état (objets de transfert de données dans les services)
  • Vous souhaitez insérer une nouvelle entité qui peut avoir une référence à une autre entité qui peut mais ne peut pas encore être créée (la relation doit être marquée MERGE). Par exemple, insérer une nouvelle photo avec une référence à un nouvel album ou à un album préexistant.

Quelle est la différence entre est géré par E et PC contient-il une version gérée de E?
GingerBeer

5

Scénario X:

Table: Spitter (One), Table: Spittles (Many) (Spittles est propriétaire de la relation avec un FK: spitter_id)

Ce scénario entraîne la sauvegarde du Spitter et des deux Spittles comme s'ils appartenaient au même Spitter.

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

Scénario Y:

Cela sauvera le Spitter, sauvera les 2 Spittles Mais ils ne référenceront pas le même Spitter!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

1
Le cracheur est un objet tiré du livre "Spring in Action" troisième édition de Graig Walls. Les cracheurs sont des personnes qui disent quelque chose et leur crachat est ce qu'ils disent réellement. Donc, un Spitter a beaucoup de crachats signifie qu'il a une liste de cordes.
George Papatheodorou

1
Vous auriez pu utiliser un exemple un peu plus lisible sans lire Spring in Action ...
wonderb0lt

1
En fait, vous n'avez pas besoin de savoir ce qu'est un crachat ou un cracheur, car en haut, il est écrit que Spitter est une table, spitter est une autre table qui possède .. ceci et cela ...
George Papatheodorou

3

Une autre observation:

merge()ne se souciera d'un identifiant généré automatiquement (testé sur IDENTITYet SEQUENCE) que lorsqu'un enregistrement avec un tel identifiant existe déjà dans votre table. Dans ce cas merge(), essaiera de mettre à jour l'enregistrement. Si, cependant, un identifiant est absent ou ne correspond à aucun enregistrement existant, merge()l'ignorera complètement et demandera à une base de données d'en allouer un nouveau. C'est parfois une source de nombreux bugs. Ne pas utiliser merge()pour forcer un identifiant pour un nouvel enregistrement.

persist()d'un autre côté, il ne vous permettra même pas de lui transmettre un identifiant. Cela échouera immédiatement. Dans mon cas, c'est:

Causée par: org.hibernate.PersistentObjectException: entité détachée passée pour persister

hibernate-jpa javadoc a un indice:

Lancers francs : javax.persistence.EntityExistsException - si l'entité existe déjà. (Si l'entité existe déjà, l'EntityExistsException peut être levée lorsque l'opération de persistance est invoquée, ou l'EntityExistsException ou une autre PersistenceException peut être levée au moment du vidage ou de la validation.)


2
Si vous n'utilisez pas d'ID générés automatiquement, vous devez attribuer manuellement un ID à votre nouvelle entité. persist()ne se plaindra pas d'avoir un ID, il ne se plaint que lorsque quelque chose avec le même ID est déjà dans la base de données.
h

1

Vous êtes peut-être venu ici pour savoir quand utiliser la persistance et quand utiliser la fusion . Je pense que cela dépend de la situation: quelle est la probabilité que vous ayez besoin de créer un nouvel enregistrement et à quel point est-il difficile de récupérer des données persistantes.

Supposons que vous puissiez utiliser une clé / un identifiant naturel.

  • Les données doivent être conservées, mais de temps en temps un enregistrement existe et une mise à jour est nécessaire. Dans ce cas, vous pouvez essayer une persistance et si elle lève une exception EntityExistsException, vous la recherchez et combinez les données:

    essayez {entityManager.persist (entity)}

    catch (exception EntityExistsException) {/ * récupérer et fusionner * /}

  • Les données persistantes doivent être mises à jour, mais de temps en temps, il n'y a pas encore d'enregistrement pour les données. Dans ce cas, vous le recherchez et effectuez une persistance si l'entité est manquante:

    entity = entityManager.find (clé);

    if (entity == null) {entityManager.persist (entity); }

    sinon {/ * fusionner * /}

Si vous n'avez pas de clé / identificateur naturel, vous aurez plus de mal à déterminer si l'entité existe ou non, ou comment la rechercher.

Les fusions peuvent également être traitées de deux manières:

  1. Si les modifications sont généralement minimes, appliquez-les à l'entité gérée.
  2. Si des modifications sont courantes, copiez l'ID de l'entité persistante, ainsi que les données non modifiées. Appelez ensuite EntityManager :: merge () pour remplacer l'ancien contenu.

0

persist (entity) doit être utilisé avec des entités totalement nouvelles, pour les ajouter à la base de données (si l'entité existe déjà dans la base de données, il y aura un lancer EntityExistsException).

merge (entity) doit être utilisé pour remettre l'entité au contexte de persistance si l'entité a été détachée et a été modifiée.

La persistance est probablement la génération de l'instruction SQL INSERT et la fusion de l'instruction SQL UPDATE (mais je ne suis pas sûr).


Ceci est une erreur. Si vous appelez merge (e) sur un nouveau e, il doit être conservé.
Pedro Lamarão


À partir de la spécification JPA version 2.1, section 3.2.7.1, deuxième puce: "Si X est une nouvelle instance d'entité, une nouvelle instance d'entité gérée X 'est créée et l'état de X est copié dans la nouvelle instance d'entité gérée X'."
Pedro Lamarão
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.