EntityManager.merge()
kann neue Objekte einfügen und vorhandene aktualisieren.
Warum sollte man verwenden wollen persist()
(was nur neue Objekte erstellen kann)?
jpa
merge
entitymanager
persist
java-persistence-api
Aaron Digulla
quelle
quelle
Antworten:
In beiden Fällen wird eine Entität zu einem PersistenceContext hinzugefügt. Der Unterschied besteht darin, was Sie anschließend mit der Entität tun.
Persist nimmt eine Entitätsinstanz, fügt sie dem Kontext hinzu und verwaltet diese Instanz (dh zukünftige Aktualisierungen der Entität werden nachverfolgt).
Zusammenführen gibt die verwaltete Instanz zurück, mit der der Status zusammengeführt wurde. Es gibt etwas zurück, was in PersistenceContext vorhanden ist, oder erstellt eine neue Instanz Ihrer Entität. In jedem Fall wird der Status von der angegebenen Entität kopiert und die verwaltete Kopie zurückgegeben. Die von Ihnen übergebene Instanz wird nicht verwaltet (alle von Ihnen vorgenommenen Änderungen sind nicht Teil der Transaktion - es sei denn, Sie rufen die Zusammenführung erneut auf). Sie können die zurückgegebene Instanz (verwaltete) verwenden.
Vielleicht hilft ein Codebeispiel.
Szenario 1 und 3 sind ungefähr gleichwertig, aber es gibt Situationen, in denen Sie Szenario 2 verwenden möchten.
quelle
merge
die vollständige Kopie eines Objekts vor der Verwaltung einen Leistungseinbruch?@GeneratedId
kann ich sie in Szenario 2 bekommen?Das Fortbestehen und Zusammenführen dient zwei verschiedenen Zwecken (sie sind überhaupt keine Alternativen).
(bearbeitet, um Informationen zu Unterschieden zu erweitern)
fortdauern:
verschmelzen:
persist () Effizienz:
persist () Semantik:
Beispiel:
Auf diese Weise existiert nur 1 angehängtes Objekt für ein Register im Entitätsmanager.
merge () für eine Entität mit einer ID ist ungefähr so:
Wenn eine Verbindung zu MySQL merge () genauso effizient sein könnte wie persist (), wenn ein Aufruf von INSERT mit der Option ON DUPLICATE KEY UPDATE verwendet wird, ist JPA eine Programmierung auf sehr hoher Ebene, und Sie können nicht davon ausgehen, dass dies überall der Fall sein wird.
quelle
em.persist(x)
mitx = em.merge(x)
?merge()
kann auch einEntityExistsException
RuntimeException
, aber es wird im Javadoc nicht erwähnt.Wenn Sie den zugewiesenen Generator verwenden, kann die Verwendung von Merge anstelle von Persist eine redundante SQL-Anweisung verursachen und somit die Leistung beeinträchtigen.
Das Aufrufen der Zusammenführung für verwaltete Entitäten ist ebenfalls ein Fehler, da verwaltete Entitäten automatisch von Hibernate verwaltet werden und ihr Status beim Löschen des Persistenzkontexts durch den Dirty-Checking-Mechanismus mit dem Datenbankdatensatz synchronisiert wird .
Um zu verstehen, wie dies alles funktioniert, sollten Sie zunächst wissen, dass Hibernate die Entwickler-Denkweise von SQL-Anweisungen zu Entitätsstatusübergängen verschiebt .
Sobald eine Entität von Hibernate aktiv verwaltet wird, werden alle Änderungen automatisch an die Datenbank weitergegeben.
Der Ruhezustand überwacht derzeit angeschlossene Entitäten. Damit eine Entität verwaltet werden kann, muss sie sich im richtigen Entitätsstatus befinden.
Um die JPA-Statusübergänge besser zu verstehen, können Sie das folgende Diagramm visualisieren:
Oder wenn Sie die Hibernate-spezifische API verwenden:
Wie in den obigen Diagrammen dargestellt, kann sich eine Entität in einem der folgenden vier Zustände befinden:
Neu (vorübergehend)
Ein neu erstelltes Objekt, das noch nie einem Ruhezustand
Session
(akaPersistence Context
) zugeordnet wurde und keiner Datenbanktabellenzeile zugeordnet ist, befindet sich im Status Neu (vorübergehend).Um persistent zu werden, müssen wir entweder die
EntityManager#persist
Methode explizit aufrufen oder den transitiven Persistenzmechanismus verwenden.Dauerhaft (verwaltet)
Eine persistente Entität wurde einer Datenbanktabellenzeile zugeordnet und wird vom aktuell ausgeführten Persistenzkontext verwaltet. Jede an einer solchen Entität vorgenommene Änderung wird erkannt und an die Datenbank weitergegeben (während der Sitzungsspülzeit). Mit Hibernate müssen wir keine INSERT / UPDATE / DELETE-Anweisungen mehr ausführen. Der Ruhezustand verwendet einen Transaktions-Write-Behind- Arbeitsstil und Änderungen werden im allerletzten verantwortlichen Moment während der aktuellen
Session
Spülzeit synchronisiert .Freistehend
Sobald der aktuell ausgeführte Persistenzkontext geschlossen ist, werden alle zuvor verwalteten Entitäten getrennt. Aufeinanderfolgende Änderungen werden nicht mehr verfolgt und es findet keine automatische Datenbanksynchronisierung statt.
Um eine getrennte Entität einer aktiven Ruhezustandssitzung zuzuordnen, können Sie eine der folgenden Optionen auswählen:
Wiederanbringen
Der Ruhezustand (aber nicht JPA 2.1) unterstützt das erneute Anhängen über die Aktualisierungsmethode "Sitzung #". Eine Ruhezustandssitzung kann nur ein Entitätsobjekt für eine bestimmte Datenbankzeile zuordnen. Dies liegt daran, dass der Persistenzkontext als speicherinterner Cache (Cache der ersten Ebene) fungiert und einem bestimmten Schlüssel (Entitätstyp und Datenbankkennung) nur ein Wert (Entität) zugeordnet ist. Eine Entität kann nur dann erneut zugeordnet werden, wenn der aktuellen Ruhezustandssitzung noch kein anderes JVM-Objekt (das mit derselben Datenbankzeile übereinstimmt) bereits zugeordnet ist.
Zusammenführen
Durch die Zusammenführung wird der Status der getrennten Entität (Quelle) in eine Instanz einer verwalteten Entität (Ziel) kopiert. Wenn die zusammengeführte Entität in der aktuellen Sitzung keine Entsprechung hat, wird eine aus der Datenbank abgerufen. Die Instanz des getrennten Objekts bleibt auch nach dem Zusammenführungsvorgang weiterhin getrennt.
Entfernt
Obwohl JPA verlangt, dass nur verwaltete Entitäten entfernt werden dürfen, kann Hibernate auch getrennte Entitäten löschen (jedoch nur über einen Aufruf der Methode Session # delete). Eine entfernte Entität ist nur zum Löschen geplant und die eigentliche DELETE-Anweisung der Datenbank wird während der Sitzungsspülzeit ausgeführt.
quelle
Ich bemerkte, dass ich bei der Verwendung für jedes
em.merge
eineSELECT
Anweisung erhieltINSERT
, auch wenn es kein Feld gab, das JPA für mich generierte - das Primärschlüsselfeld war eine UUID, die ich selbst festgelegt hatte. Ich wechselte zuem.persist(myEntityObject)
und bekam dann nurINSERT
Aussagen.quelle
merge()
. Ich hatte eine PostgreSQL-Datenbank mit komplizierter Ansicht : Die Ansicht aggregierte Daten aus mehreren Tabellen (die Tabellen hatten identische Struktur, aber unterschiedliche Namen). Also versuchte JPA diesmerge()
, aber tatsächlich wurde JPA zuerst erstelltSELECT
(Datenbank aufgrund von Ansichtseinstellungen konnte mehrere Datensätze mit demselben Primärschlüssel aus verschiedenen Tabellen zurückgeben!), Dann schlug JPA (Ruhezustand war eine Implementierung) fehl: Es gibt mehrere Datensätze mit demselben Schlüssel (org.hibernate.HibernateException: More than one row with the given identifier was found
). In meinem Fallpersist()
hat mir geholfen.Die JPA-Spezifikation sagt Folgendes über
persist()
.Die Verwendung
persist()
wäre daher geeignet, wenn das Objekt kein abgetrenntes Objekt sein sollte. Möglicherweise möchten Sie den Code lieber werfen lassen,PersistenceException
damit er schnell fehlschlägt.Obwohl die Spezifikation unklar ist ,
persist()
kann dies@GeneratedValue
@Id
für ein Objekt festgelegt werden.merge()
muss jedoch ein Objekt mit dem@Id
bereits generierten haben.quelle
merge()
muss jedoch ein Objekt mit dem@Id
bereits generierten haben . ". Wenn der EntityManager keinen Wert für das Feld der Objekt-ID findet, wird er in der Datenbank beibehalten (eingefügt).Einige weitere Details zur Zusammenführung, die Ihnen bei der Verwendung der Zusammenführung helfen, bleiben bestehen:
Alle oben genannten Informationen stammen aus "Pro JPA 2 Mastering the Java ™ Persistence API" von Mike Keith und Merrick Schnicariol. Kapitel 6. Ablösen und Zusammenführen von Abschnitten. Dieses Buch ist eigentlich ein zweites Buch, das von Autoren JPA gewidmet wurde. Dieses neue Buch enthält viele neue Informationen als die früheren. Ich habe wirklich empfohlen, dieses Buch für diejenigen zu lesen, die ernsthaft mit JPA zu tun haben. Es tut mir leid, dass ich meine erste Antwort einstimmig veröffentlicht habe.
quelle
Es gibt einige weitere Unterschiede zwischen
merge
undpersist
(ich werde die bereits hier veröffentlichten noch einmal aufzählen):D1.
merge
macht die übergebene Entität nicht verwaltet, sondern gibt eine andere verwaltete Instanz zurück.persist
Auf der anderen Seite wird die übergebene Entität verwaltet:D2. Wenn Sie eine Entität entfernen und dann entscheiden, die Entität zurückzuhalten, können Sie dies nur mit persist () tun, da
merge
eineIllegalArgumentException
.D3. Wenn Sie sich entschieden haben, Ihre IDs manuell zu verwalten (z. B. mithilfe von UUIDs),
merge
löst eine Operation nachfolgendeSELECT
Abfragen aus, um nach vorhandenen Entitäten mit dieser ID zu suchenpersist
ohne diese Abfragen zu benötigen.D4. Es gibt Fälle, in denen Sie dem Code, der Ihren Code aufruft, einfach nicht vertrauen. Um sicherzustellen, dass keine Daten aktualisiert, sondern eingefügt werden, müssen Sie verwenden
persist
.quelle
Ich habe lazyLoading-Ausnahmen für meine Entität erhalten, weil ich versucht habe, auf eine faul geladene Sammlung zuzugreifen, die sich in einer Sitzung befand.
Was ich tun würde, war in einer separaten Anfrage, die Entität aus der Sitzung abzurufen und dann zu versuchen, auf eine Sammlung auf meiner JSP-Seite zuzugreifen, was problematisch war.
Um dies zu vermeiden, habe ich dieselbe Entität in meinem Controller aktualisiert und an meinen JSP übergeben, obwohl ich mir vorstelle, dass ich beim erneuten Speichern in der Sitzung auch darauf zugreifen kann
SessionScope
und kein a auslösen kannLazyLoadingException
Änderung von Beispiel 2 auslösen kann:Folgendes hat für mich funktioniert:
quelle
Ich fand diese Erklärung aus den Hibernate-Dokumenten aufschlussreich, da sie einen Anwendungsfall enthalten:
Von: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html
quelle
Beim Durchgehen der Antworten fehlen einige Details bezüglich "Cascade" und ID-Generierung. Siehe Frage
Erwähnenswert ist auch, dass Sie separate
Cascade
Anmerkungen zum Zusammenführen und Fortbestehen haben können:Cascade.MERGE
undCascade.PERSIST
die nach dem verwendeten Verfahren behandelt werden.Die Spezifikation ist dein Freund;)
quelle
Bestehende Einheiten
Im Gegensatz zur Zusammenführungsmethode ist die Persist-Methode ziemlich einfach und intuitiv. Das häufigste Szenario für die Verwendung der Persist-Methode kann wie folgt zusammengefasst werden:
"Eine neu erstellte Instanz der Entitätsklasse wird an die persist-Methode übergeben. Nachdem diese Methode zurückgegeben wurde, wird die Entität verwaltet und zum Einfügen in die Datenbank geplant. Dies kann bei oder vor dem Festschreiben der Transaktion oder beim Aufruf der Flush-Methode geschehen. Wenn die Entität über eine Beziehung, die mit der PERSIST-Kaskadenstrategie markiert ist, auf eine andere Entität verweist, wird dieses Verfahren auch auf diese angewendet. "
Die Spezifikation geht mehr auf Details ein, das Erinnern an sie ist jedoch nicht entscheidend, da diese Details nur mehr oder weniger exotische Situationen abdecken.
Zusammenführen von Einheiten
Im Vergleich zu persist ist die Beschreibung des Zusammenführungsverhaltens nicht so einfach. Es gibt kein Hauptszenario, wie es im Fall von persist der Fall ist, und ein Programmierer muss sich alle Szenarien merken, um einen korrekten Code zu schreiben. Es scheint mir, dass die JPA-Designer eine Methode haben wollten, deren Hauptanliegen darin besteht, getrennte Entitäten zu behandeln (im Gegensatz zu der Persist-Methode, die sich hauptsächlich mit neu erstellten Entitäten befasst). Die Hauptaufgabe der Zusammenführungsmethode besteht darin, den Status von einer zu übertragen Nicht verwaltete Entität (als Argument übergeben) an ihr verwaltetes Gegenstück im Persistenzkontext. Diese Aufgabe gliedert sich jedoch weiter in mehrere Szenarien, die die Verständlichkeit des Verhaltens der Gesamtmethode verschlechtern.
Anstatt Absätze aus der JPA-Spezifikation zu wiederholen, habe ich ein Flussdiagramm erstellt, das das Verhalten der Zusammenführungsmethode schematisch darstellt:
Also, wann sollte ich persist verwenden und wann zusammenführen?
fortdauern
verschmelzen
quelle
Szenario X:
Tabelle: Spitter (Eins), Tabelle: Spittles (Viele) (Spittles ist Eigentümer der Beziehung zu einem FK: spitter_id)
Dieses Szenario führt zum Speichern: Der Spitter und beide Spittles, als ob sie demselben Spitter gehören.
Szenario Y:
Dies wird den Spitter retten, wird die 2 Spittles retten, aber sie werden nicht auf denselben Spitter verweisen!
quelle
Eine weitere Beobachtung:
merge()
kümmert sich nur um eine automatisch generierte ID (getestet aufIDENTITY
undSEQUENCE
), wenn ein Datensatz mit einer solchen ID bereits in Ihrer Tabelle vorhanden ist. In diesem Fallmerge()
wird versucht, den Datensatz zu aktualisieren. Wenn jedoch eine ID fehlt oder nicht mit vorhandenen Datensätzen übereinstimmt,merge()
wird sie vollständig ignoriert und eine Datenbank aufgefordert, eine neue zuzuweisen. Dies ist manchmal eine Quelle für viele Fehler. Verwenden Sie diese Option nichtmerge()
, um eine ID für einen neuen Datensatz zu erzwingen.persist()
Auf der anderen Seite können Sie niemals einen Ausweis an ihn weitergeben. Es wird sofort fehlschlagen. In meinem Fall ist es:hibernate-jpa javadoc hat einen Hinweis:
quelle
persist()
beschwert sich nicht, dass es eine ID hat, es beschwert sich nur, wenn sich bereits etwas mit derselben ID in der Datenbank befindet.Möglicherweise sind Sie hierher gekommen, um Ratschläge zu erhalten, wann persist und wann merge verwendet werden soll . Ich denke, dass es von der Situation abhängt: Wie wahrscheinlich ist es, dass Sie einen neuen Datensatz erstellen müssen, und wie schwierig ist es, persistente Daten abzurufen.
Nehmen wir an, Sie können einen natürlichen Schlüssel / Bezeichner verwenden.
Daten müssen beibehalten werden, aber hin und wieder ist ein Datensatz vorhanden und eine Aktualisierung ist erforderlich. In diesem Fall können Sie versuchen, eine Persist zu erstellen. Wenn eine EntityExistsException ausgelöst wird, suchen Sie sie und kombinieren die Daten:
try {entityManager.persist (entity)}
catch (EntityExistsException-Ausnahme) {/ * abrufen und zusammenführen * /}
Persistierte Daten müssen aktualisiert werden, aber hin und wieder gibt es noch keinen Datensatz für die Daten. In diesem Fall suchen Sie nach und führen eine Persistenz durch, wenn die Entität fehlt:
entity = entityManager.find (Schlüssel);
if (entity == null) {entityManager.persist (entity); }}
sonst {/ * zusammenführen * /}
Wenn Sie keinen natürlichen Schlüssel / Bezeichner haben, fällt es Ihnen schwerer, herauszufinden, ob die Entität vorhanden ist oder nicht, oder wie Sie sie nachschlagen können.
Die Zusammenführungen können auch auf zwei Arten behandelt werden:
quelle
persist (Entität) sollte mit völlig neuen Entitäten verwendet werden, um sie zur Datenbank hinzuzufügen (wenn die Entität bereits in der Datenbank vorhanden ist, wird EntityExistsException ausgelöst).
Merge (Entität) sollte verwendet werden, um die Entität wieder in den Persistenzkontext zu versetzen, wenn die Entität getrennt und geändert wurde.
Wahrscheinlich generiert persist die INSERT-SQL-Anweisung und führt die UPDATE-SQL-Anweisung zusammen (aber ich bin mir nicht sicher).
quelle