JPA EntityManager: Warum persist () anstelle von merge () verwenden?

Antworten:

1615

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.

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)

Szenario 1 und 3 sind ungefähr gleichwertig, aber es gibt Situationen, in denen Sie Szenario 2 verwenden möchten.

Mike
quelle
3
@dma_k: Sieht so aus, als würden Sie den Ruhezustand verwenden. Ich bin mit Hibernate weniger vertraut als mit JPA. Wenn Sie jedoch in JPA EntityManager.persist () aufrufen und eine getrennte Entität übergeben, erhalten Sie: a) sofort eine EntityExistsException oder b) eine weitere PersistenceException zum Flush / Commit-Zeitpunkt. Vielleicht habe ich die Frage hier falsch verstanden?
Mike
49
Diese Antwort könnte verbessert werden, wenn sie auch die Fälle abdeckt, in denen die zusammengeführte / persistierte Entität bereits im persistenten Kontext existiert (oder zumindest klarer gemacht hat, dass sie nur das Verhalten beschreibt, wenn die persistierte / zusammengeführte Entität noch nicht existiert)
Henry
2
Ist eine Methode performanter? Vielleicht hat mergedie vollständige Kopie eines Objekts vor der Verwaltung einen Leistungseinbruch?
Kevin Meredith
2
Was ist mit Ausweisen? Wenn ich eine habe, @GeneratedIdkann ich sie in Szenario 2 bekommen?
Rascio
7
Mike: "Zusammenführen erstellt eine neue Instanz ...": Dies ist nicht immer der Fall. Wenn der EntityManager eine bereits verwaltete Entität in seinem Kontext findet, gibt er diese Instanz zurück (nachdem die Felder aktualisiert wurden). Bitte bearbeiten Sie Ihre Antwort, dann werde ich dafür stimmen.
Heri
181

Das Fortbestehen und Zusammenführen dient zwei verschiedenen Zwecken (sie sind überhaupt keine Alternativen).

(bearbeitet, um Informationen zu Unterschieden zu erweitern)

fortdauern:

  • Fügen Sie ein neues Register in die Datenbank ein
  • Hängen Sie das Objekt an den Entitätsmanager an.

verschmelzen:

  • Suchen Sie ein angehängtes Objekt mit derselben ID und aktualisieren Sie es.
  • Wenn vorhanden, aktualisieren Sie das bereits angehängte Objekt und geben Sie es zurück.
  • Wenn nicht vorhanden, fügen Sie das neue Register in die Datenbank ein.

persist () Effizienz:

  • Das Einfügen eines neuen Registers in eine Datenbank könnte effizienter sein als das Zusammenführen ().
  • Das ursprüngliche Objekt wird nicht dupliziert.

persist () Semantik:

  • Es stellt sicher, dass Sie versehentlich einfügen und nicht aktualisieren.

Beispiel:

{
    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);
}

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:

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;
}

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.

Josep Panadero
quelle
Können Sie einen Fall nennen , wo es nicht gültig ist , zu ersetzen , em.persist(x)mit x = em.merge(x)?
Aaron Digulla
20
persist () kann eine EntityExistsException auslösen. Wenn Sie sicher sein möchten, dass Ihr Code eine Einfügung und keine Aktualisierung der Daten ausführt, müssen Sie persist verwenden.
Josep Panadero
1
merge()kann auch einEntityExistsException
Sean
1
@None Es könnte sein, weil es ein ist RuntimeException, aber es wird im Javadoc nicht erwähnt.
Martin
154

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:

Übergänge des JPA-Entitätsstatus

Oder wenn Sie die Hibernate-spezifische API verwenden:

Zustandsübergänge im Ruhezustand

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(aka Persistence 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#persistMethode 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 SessionSpü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.

Vlad Mihalcea
quelle
Können Sie einen Blick auf stackoverflow.com/questions/46214322/… werfen ?
gstackoverflow
@gstackoverflow Die Antwort, die Sie erhalten haben, ist die richtige. Weitere Informationen finden Sie in diesem Artikel oder in meinem Buch High-Performance Java Persistence .
Vlad Mihalcea
Somit gibt es keine Möglichkeit, die Operationsreihenfolge für Orphanremoval zu ändern = true?
gstackoverflow
Ihr Artikel über die Betriebsreihenfolge im Normalfall. Meine Frage speziell für orphanRemoval
gstackoverflow
1
Tatsache ist, dass es unmöglich ist, den Ruhezustand mit einem solchen Diagramm zu erklären. Warum können Sie die Sitzung nach dem Trennen nicht spülen? Was passiert, wenn Sie versuchen, eine bereits persistierte Entität zu speichern? Warum ist dieses Flush-Verhalten anders, wenn es um Speichern und Fortbestehen geht? Es gibt 1000 solcher Fragen, zu denen niemand eine klare Logik hat.
GingerBeer
37

Ich bemerkte, dass ich bei der Verwendung für jedes em.mergeeine SELECTAnweisung erhielt INSERT, 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 zu em.persist(myEntityObject)und bekam dann nur INSERTAussagen.

Sarah Vessels
quelle
3
Sinnvoll, da Sie die IDs zuweisen und der JPA-Container keine Ahnung hat, woher Sie diese haben. Es besteht eine (kleine) Wahrscheinlichkeit, dass das Objekt bereits in der Datenbank vorhanden ist, beispielsweise in einem Szenario, in dem mehrere Anwendungen in dieselbe Datenbank schreiben.
Aaron Digulla
Ich habe ein ähnliches Problem mit 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 dies merge(), aber tatsächlich wurde JPA zuerst erstellt SELECT(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 Fall persist()hat mir geholfen.
flaz14
29

Die JPA-Spezifikation sagt Folgendes über persist().

Wenn X ein getrenntes Objekt ist, EntityExistsExceptionkann das Objekt ausgelöst werden, wenn die Persist-Operation aufgerufen wird, oder das EntityExistsExceptionoder ein anderes Objekt PersistenceExceptionkann zur Flush- oder Commit-Zeit ausgelöst werden.

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, PersistenceExceptiondamit er schnell fehlschlägt.

Obwohl die Spezifikation unklar ist , persist()kann dies @GeneratedValue @Idfür ein Objekt festgelegt werden. merge()muss jedoch ein Objekt mit dem @Idbereits generierten haben.

Raedwald
quelle
5
+1 für " 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).
Omar
Ich habe das zuerst nicht verstanden, da mir die Zustände nicht klar waren. Hoffe das hilft jemandem wie bei mir. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz
1
@GeneratedValue hat keine andere Implikation für merge () und persist ()
SandeepGodara
17

Einige weitere Details zur Zusammenführung, die Ihnen bei der Verwendung der Zusammenführung helfen, bleiben bestehen:

Die Rückgabe einer anderen verwalteten Instanz als der ursprünglichen Entität ist ein wichtiger Bestandteil des Zusammenführungsprozesses. Wenn im Persistenzkontext bereits eine Entitätsinstanz mit demselben Bezeichner vorhanden ist, überschreibt der Anbieter seinen Status mit dem Status der zusammengeführten Entität. Die bereits vorhandene verwaltete Version muss jedoch an den Client zurückgegeben werden, damit dies möglich ist benutzt. Wenn der Anbieter die Employee-Instanz im Persistenzkontext nicht aktualisiert hat, werden alle Verweise auf diese Instanz nicht mehr mit dem neuen Status vereinbar, der zusammengeführt wird.

Wenn merge () für eine neue Entität aufgerufen wird, verhält es sich ähnlich wie die persist () -Operation. Es fügt die Entität dem Persistenzkontext hinzu, aber anstatt die ursprüngliche Entitätsinstanz hinzuzufügen, erstellt es eine neue Kopie und verwaltet stattdessen diese Instanz. Die durch die Operation merge () erstellte Kopie bleibt bestehen, als ob die Methode persist () darauf aufgerufen worden wäre.

Bei Vorhandensein von Beziehungen versucht die Operation merge (), die verwaltete Entität so zu aktualisieren, dass sie auf verwaltete Versionen der Entitäten verweist, auf die von der getrennten Entität verwiesen wird. Wenn die Entität eine Beziehung zu einem Objekt hat, das keine dauerhafte Identität hat, ist das Ergebnis der Zusammenführungsoperation undefiniert. Einige Anbieter erlauben möglicherweise, dass die verwaltete Kopie auf das nicht persistente Objekt verweist, während andere möglicherweise sofort eine Ausnahme auslösen. In diesen Fällen kann die Operation merge () optional kaskadiert werden, um das Auftreten einer Ausnahme zu verhindern. Wir werden uns später in diesem Abschnitt mit der Kaskadierung der Operation merge () befassen. Wenn eine zusammengeführte Entität auf eine entfernte Entität verweist, wird eine IllegalArgumentException-Ausnahme ausgelöst.

Lazy-Loading-Beziehungen sind ein Sonderfall bei der Zusammenführung. Wenn eine Lazy-Loading-Beziehung für eine Entität nicht ausgelöst wurde, bevor sie getrennt wurde, wird diese Beziehung beim Zusammenführen der Entität ignoriert. Wenn die Beziehung während der Verwaltung ausgelöst und dann auf Null gesetzt wurde, während die Entität getrennt wurde, wird die Beziehung in der verwalteten Version der Entität ebenfalls während der Zusammenführung gelöscht. "

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.

Khurshed Salimov
quelle
17

Es gibt einige weitere Unterschiede zwischen mergeund persist(ich werde die bereits hier veröffentlichten noch einmal aufzählen):

D1. mergemacht die übergebene Entität nicht verwaltet, sondern gibt eine andere verwaltete Instanz zurück. persistAuf der anderen Seite wird die übergebene Entität verwaltet:

//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. Wenn Sie eine Entität entfernen und dann entscheiden, die Entität zurückzuhalten, können Sie dies nur mit persist () tun, da mergeeineIllegalArgumentException .

D3. Wenn Sie sich entschieden haben, Ihre IDs manuell zu verwalten (z. B. mithilfe von UUIDs), merge löst eine Operation nachfolgende SELECTAbfragen 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.

Andrei ich
quelle
8

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 SessionScopeund kein a auslösen kannLazyLoadingException Änderung von Beispiel 2 auslösen kann:

Folgendes hat für mich funktioniert:

// 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!!
logixplayer
quelle
7

Ich fand diese Erklärung aus den Hibernate-Dokumenten aufschlussreich, da sie einen Anwendungsfall enthalten:

Die Verwendung und Semantik von merge () scheint für neue Benutzer verwirrend zu sein. Erstens sollten Sie merge () überhaupt nicht verwenden müssen , solange Sie nicht versuchen, den in einem Entitätsmanager geladenen Objektstatus in einem anderen neuen Entitätsmanager zu verwenden . Einige ganze Anwendungen werden diese Methode niemals verwenden.

Normalerweise wird merge () im folgenden Szenario verwendet:

  • Die Anwendung lädt ein Objekt in den ersten Entitätsmanager
  • Das Objekt wird an die Präsentationsschicht übergeben
  • Einige Änderungen werden am Objekt vorgenommen
  • Das Objekt wird an die Geschäftslogikschicht zurückgegeben
  • Die Anwendung behält diese Änderungen bei, indem sie merge () in einem zweiten Entitätsmanager aufruft

Hier ist die genaue Semantik von merge ():

  • Wenn eine verwaltete Instanz mit derselben Kennung vorhanden ist, die derzeit dem Persistenzkontext zugeordnet ist, kopieren Sie den Status des angegebenen Objekts auf die verwaltete Instanz
  • Wenn dem Persistenzkontext derzeit keine verwaltete Instanz zugeordnet ist, versuchen Sie, sie aus der Datenbank zu laden, oder erstellen Sie eine neue verwaltete Instanz
  • Die verwaltete Instanz wird zurückgegeben
  • Die angegebene Instanz wird nicht mit dem Persistenzkontext verknüpft, sondern bleibt getrennt und wird normalerweise verworfen

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

Ray Hulha
quelle
6

Beim Durchgehen der Antworten fehlen einige Details bezüglich "Cascade" und ID-Generierung. Siehe Frage

Erwähnenswert ist auch, dass Sie separate CascadeAnmerkungen zum Zusammenführen und Fortbestehen haben können: Cascade.MERGEundCascade.PERSIST die nach dem verwendeten Verfahren behandelt werden.

Die Spezifikation ist dein Freund;)

Ioannis Deligiannis
quelle
6

JPA ist unbestreitbar eine große Vereinfachung im Bereich von Unternehmensanwendungen, die auf der Java-Plattform basieren. Als Entwickler, der sich mit den Feinheiten der alten Entity Beans in J2EE auseinandersetzen musste, sehe ich die Aufnahme von JPA in die Java EE-Spezifikationen als einen großen Sprung nach vorne. Während ich mich jedoch eingehender mit den JPA-Details befasse, finde ich Dinge, die nicht so einfach sind. In diesem Artikel beschäftige ich mich mit dem Vergleich der Merge- und Persist-Methoden des EntityManager, deren überlappendes Verhalten nicht nur bei Neulingen Verwirrung stiften kann. Darüber hinaus schlage ich eine Verallgemeinerung vor, bei der beide Methoden als Sonderfälle eines allgemeineren Methodenkombinats betrachtet werden.

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

Geben Sie hier die Bildbeschreibung ein

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:

Geben Sie hier die Bildbeschreibung ein

Also, wann sollte ich persist verwenden und wann zusammenführen?

fortdauern

  • Sie möchten, dass die Methode immer eine neue Entität erstellt und niemals eine Entität aktualisiert. Andernfalls löst die Methode eine Ausnahme als Folge einer Verletzung der Eindeutigkeit des Primärschlüssels aus.
  • Stapelprozesse, die Entitäten zustandsbehaftet behandeln (siehe Gateway-Muster).
  • Leistungsoptimierung

verschmelzen

  • Sie möchten, dass die Methode eine Entität in die Datenbank einfügt oder aktualisiert.
  • Sie möchten Entitäten zustandslos behandeln (Datenübertragungsobjekte in Diensten)
  • Sie möchten eine neue Entität einfügen, die möglicherweise einen Verweis auf eine andere Entität enthält, die möglicherweise noch nicht erstellt wurde (die Beziehung muss als MERGE gekennzeichnet sein). Beispiel: Einfügen eines neuen Fotos mit einem Verweis auf ein neues oder ein bereits vorhandenes Album.
Amit Gujarathi
quelle
Was ist der Unterschied zwischen E-verwaltet und Enthält der PC eine verwaltete Version von E?
GingerBeer
5

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.

        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!!

Szenario Y:

Dies wird den Spitter retten, wird die 2 Spittles retten, aber sie werden nicht auf denselben Spitter verweisen!

        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!!
George Papatheodorou
quelle
1
Der Spitter ist ein Objekt aus dem Buch "Spring in Action", dritte Ausgabe von Graig Walls. Spitter sind Personen, die etwas sagen, und ihr Spucke ist das, was sie tatsächlich sagen. Ein Spitter hat also viele Spucknäpfe, was bedeutet, dass er eine Liste von Saiten hat.
George Papatheodorou
1
Sie hätten ein Beispiel verwenden können, das etwas besser lesbar ist, ohne Spring in Action zu lesen ...
wunderb0lt
1
Sie müssen eigentlich nicht wissen, was ein Speichel oder ein Spitter ist, da oben geschrieben steht, dass Spitter ein Tisch ist, Spitter ein anderer Tisch, der besitzt ... dies und das ...
George Papatheodorou
3

Eine weitere Beobachtung:

merge()kümmert sich nur um eine automatisch generierte ID (getestet auf IDENTITYund SEQUENCE), wenn ein Datensatz mit einer solchen ID bereits in Ihrer Tabelle vorhanden ist. In diesem Fall merge()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 nicht merge(), 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:

Auslöser: org.hibernate.PersistentObjectException: Abgetrennte Entität, die an persist übergeben wurde

hibernate-jpa javadoc hat einen Hinweis:

Wirft : javax.persistence.EntityExistsException - wenn das Unternehmen bereits vorhanden ist . (Wenn die Entität bereits vorhanden ist, wird die EntityExistsException möglicherweise ausgelöst, wenn die Persist-Operation aufgerufen wird, oder die EntityExistsException oder eine andere PersistenceException wird möglicherweise zum Löschen oder Festschreiben ausgelöst.)

Yuranos
quelle
2
Wenn Sie keine automatisch generierten IDs verwenden, müssen Sie Ihrer neuen Entität manuell eine ID geben. persist()beschwert sich nicht, dass es eine ID hat, es beschwert sich nur, wenn sich bereits etwas mit derselben ID in der Datenbank befindet.
Stunden
1

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:

  1. Wenn die Änderungen normalerweise gering sind, wenden Sie sie auf die verwaltete Entität an.
  2. Wenn Änderungen häufig sind, kopieren Sie die ID von der persistierten Entität sowie die unveränderten Daten. Rufen Sie dann EntityManager :: merge () auf, um den alten Inhalt zu ersetzen.
Peter Willems
quelle
0

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

Krystian
quelle
Das ist falsch. Wenn Sie merge (e) für ein neues e aufrufen, muss es beibehalten werden.
Pedro Lamarão
Aus der JPA-Spezifikation Version 2.1, Abschnitt 3.2.7.1, zweiter Aufzählungspunkt: "Wenn X eine neue Entitätsinstanz ist, wird eine neue verwaltete Entitätsinstanz X 'erstellt und der Status von X in die neue verwaltete Entitätsinstanz X' kopiert."
Pedro Lamarão