EntityManager.merge()
peut insérer de nouveaux objets et mettre à jour ceux existants.
Pourquoi voudrait-on utiliser persist()
(qui ne peut que créer de nouveaux objets)?
jpa
merge
entitymanager
persist
java-persistence-api
Aaron Digulla
la source
la source
Réponses:
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.
Les scénarios 1 et 3 sont à peu près équivalents, mais il existe certaines situations où vous souhaitez utiliser le scénario 2.
la source
merge
la copie complète d'un objet avant de le gérer a un impact sur les performances?@GeneratedId
puis-je l'obtenir dans le scénario 2?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:
fusionner:
efficacité persist ():
sémantique persist ():
Exemple:
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:
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.
la source
em.persist(x)
avecx = em.merge(x)
?merge()
peut également lancer unEntityExistsException
RuntimeException
, mais ce n'est pas mentionné dans le Javadoc.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:
Ou si vous utilisez l'API spécifique Hibernate:
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
(akaPersistence 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#persist
mé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
Session
temps 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.
la source
J'ai remarqué que lorsque j'utilisais
em.merge
, j'obtenais uneSELECT
déclaration pour chaqueINSERT
, 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 desINSERT
déclarations.la source
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 fairemerge()
, 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, celapersist()
m'a aidé.La spécification JPA dit ce qui suit à propos de
persist()
.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 lePersistenceException
afin qu'il échoue rapidement.Bien que la spécification ne soit pas claire ,
persist()
peut définir le@GeneratedValue
@Id
pour un objet.merge()
cependant doit avoir un objet avec le@Id
déjà généré.la source
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.Quelques détails supplémentaires sur la fusion qui vous aideront à utiliser la fusion sur la persistance:
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.
la source
Il y a encore plus de différences entre
merge
etpersist
(je vais énumérer à nouveau celles déjà publiées ici):D1.
merge
ne gère pas l'entité transmise, mais renvoie plutôt une autre instance gérée.persist
de l'autre côté, l'entité transmise sera gérée:D2. Si vous supprimez une entité et décidez ensuite de la conserver, vous ne pouvez le faire qu'avec persist (), car
merge
jettera unIllegalArgumentException
.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 desSELECT
requêtes ultérieures afin de rechercher les entités existantes avec cet identifiant, tout enpersist
n'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
.la source
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
SessionScope
et ne lâchera pasLazyLoadingException
, une modification de l'exemple 2:Ce qui suit a fonctionné pour moi:
la source
J'ai trouvé cette explication des documents Hibernate éclairante, car ils contiennent un cas d'utilisation:
De: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html
la source
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
Cascade
annotations distinctes pour la fusion et la persistance:Cascade.MERGE
etCascade.PERSIST
qui seront traitées selon la méthode utilisée.La spécification est votre amie;)
la source
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. "
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:
Alors, quand dois-je utiliser persist et quand fusionner?
persister
fusionner
la source
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.
Scénario Y:
Cela sauvera le Spitter, sauvera les 2 Spittles Mais ils ne référenceront pas le même Spitter!
la source
Une autre observation:
merge()
ne se souciera d'un identifiant généré automatiquement (testé surIDENTITY
etSEQUENCE
) que lorsqu'un enregistrement avec un tel identifiant existe déjà dans votre table. Dans ce casmerge()
, 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 utilisermerge()
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:hibernate-jpa javadoc a un indice:
la source
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.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:
la source
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).
la source