1071 Stimmen

JPA EntityManager: Warum persist() statt merge() verwenden?

EntityManager.merge() kann neue Objekte einfügen und bestehende aktualisieren.

Warum sollte man die persist() (die nur neue Objekte erstellen kann)?

14 Stimmen

2 Stimmen

Wenn Sie Diagramme mögen. Siehe dies: spitballer.blogspot.in/2010/04/

1773voto

Mike Punkte 18302

In beiden Fällen wird eine Entität zu einem PersistenceContext hinzugefügt, der Unterschied liegt darin, was man anschließend mit der Entität macht.

Persist nimmt eine Entitätsinstanz, fügt sie dem Kontext hinzu und macht diese Instanz verwaltbar (d.h. zukünftige Aktualisierungen der Entität werden nachverfolgt).

Merge gibt die verwaltete Instanz zurück, mit der der Zustand zusammengeführt wurde. Es gibt etwas zurück, das im PersistenceContext existiert, oder erstellt eine neue Instanz Ihrer Entität. In jedem Fall wird der Zustand von der übergebenen Entität kopiert und eine verwaltete Kopie zurückgegeben. Die Instanz, die Sie übergeben, wird nicht verwaltet (alle Änderungen, die Sie vornehmen, werden nicht Teil der Transaktion sein - es sei denn, Sie rufen merge erneut auf). Sie können jedoch die zurückgegebene (verwaltete) Instanz 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 einige Situationen, in denen Sie Szenario 2 verwenden sollten.

4 Stimmen

Eine der besten Erklärungen, die Sie frei im Internet finden können, erklärt von MASTER

2 Stimmen

Warum sollte jemand so einen Code schreiben? Es macht für mich Sinn, Setter für eine Entität aufzurufen und DANN Merge oder Persist aufzurufen... warum sollte jemand diese aufrufen, BEVOR er einen Setter für eine Entität aufruft?

218voto

Josep Panadero Punkte 2892

Persistieren und Zusammenführen dienen zwei verschiedenen Zwecken (sie sind überhaupt keine Alternativen).

(bearbeitet, um Informationen über Unterschiede zu erweitern)

beharrlich:

  • Einfügen eines neuen Registers in die Datenbank
  • Hängen Sie das Objekt an den Entitätsmanager an.

verschmelzen:

  • Suchen Sie ein angehängtes Objekt mit der gleichen ID und aktualisieren Sie es.
  • Falls vorhanden, wird das bereits angehängte Objekt aktualisiert und zurückgegeben.
  • Wenn nicht vorhanden, fügen Sie das neue Register in die Datenbank ein.

persist() Effizienz:

  • Es könnte für das Einfügen eines neuen Registers in eine Datenbank effizienter sein als merge().
  • Es dupliziert das Originalobjekt nicht.

persist()-Semantik:

  • So können Sie sicher sein, dass Sie einfügen und nicht versehentlich 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 jedes Register im Entity Manager.

merge() für eine Entität mit einer id ist etwa 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;
}

Obwohl merge() bei einer Verbindung mit MySQL so effizient sein könnte wie persist() unter Verwendung eines Aufrufs von INSERT mit der Option ON DUPLICATE KEY UPDATE, ist JPA eine Programmierung auf sehr hohem Niveau und man kann nicht davon ausgehen, dass dies überall der Fall sein wird.

0 Stimmen

Können Sie einen Fall nennen, in dem es nicht zulässig ist, die em.persist(x) con x = em.merge(x) ?

28 Stimmen

Persist() kann eine EntityExistsException auslösen. Wenn Sie sicher sein wollen, dass Ihr Code eine Einfügung und nicht eine Aktualisierung der Daten vornimmt, müssen Sie persist verwenden.

1 Stimmen

merge() kann auch eine EntityExistsException

197voto

Vlad Mihalcea Punkte 121171

Wenn Sie den zugewiesenen Generator verwenden, müssen Sie merge anstelle von persist kann zu einer redundanten SQL-Anweisung führen und damit die Leistung beeinträchtigen.

Auch das Anrufen merge für verwaltete Entitäten ist ebenfalls ein Fehler, da verwaltete Entitäten automatisch von Hibernate verwaltet werden und ihr Zustand durch den Dirty-Checking-Mechanismus beim Flushen des Persistenzkontextes mit dem Datenbankeintrag synchronisiert wird.

Um zu verstehen, wie das alles funktioniert, sollten Sie zunächst wissen, dass Hibernate die Denkweise der Entwickler von SQL-Anweisungen auf Zustandsübergänge von Entitäten verlagert.

Sobald eine Entität aktiv von Hibernate verwaltet wird, werden alle Änderungen automatisch an die Datenbank weitergegeben.

Hibernate überwacht aktuell angehängte Entitäten. Damit eine Entität verwaltet werden kann, muss sie sich jedoch im richtigen Entitätsstatus befinden.

Um die JPA-Zustandsübergänge besser zu verstehen, können Sie sich das folgende Diagramm ansehen:

JPA entity state transitions

Oder wenn Sie die Hibernate-spezifische API verwenden:

Hibernate entity state transitions

Wie in den obigen Diagrammen dargestellt, kann eine Entität einen der folgenden vier Zustände einnehmen:

  • Neu (vorübergehend)

Ein neu erstelltes Objekt, das noch nie mit einem Hibernate Session (a.k.a. Persistence Context ) und keiner Datenbanktabellenzeile zugeordnet ist, befindet sich im Zustand Neu (transient).

Um persistent zu werden, müssen wir entweder explizit die EntityManager#persist Methode oder verwenden Sie den transitiven Persistenzmechanismus.

  • Dauerhaft (verwaltet)

    Eine persistente Entität wurde mit einer Datenbanktabellenzeile verknüpft und wird durch den aktuell laufenden Persistenzkontext verwaltet. Jede Änderung, die an einer solchen Entität vorgenommen wird, wird erkannt und an die Datenbank weitergegeben (während der Session-Flush-Zeit). Mit Hibernate müssen wir keine INSERT/UPDATE/DELETE-Anweisungen mehr ausführen. Hibernate verwendet einen transaktionalen Write-Behind-Arbeitsstil, und Änderungen werden im allerletzten verantwortlichen Moment synchronisiert, während der aktuellen Session Spülungszeit.

  • Freistehend

Sobald der aktuell laufende Persistenzkontext geschlossen wird, werden alle zuvor verwalteten Entitäten abgetrennt. Aufeinanderfolgende Änderungen werden nicht mehr verfolgt und es findet keine automatische Datenbanksynchronisation statt.

Um eine abgetrennte Entität mit einer aktiven Hibernate-Sitzung zu verknüpfen, können Sie eine der folgenden Optionen wählen:

  • Wiederbefestigung

    Hibernate (aber nicht JPA 2.1) unterstützt die Wiederanbindung durch die Methode Session#update.

    Eine Hibernate-Sitzung kann nur ein Entity-Objekt für eine bestimmte Datenbankzeile zuordnen. Dies liegt daran, dass der Persistenzkontext als In-Memory-Cache (Cache der ersten Ebene) fungiert und nur ein Wert (Entität) mit einem bestimmten Schlüssel (Entitätstyp und Datenbankkennung) verbunden ist.

    Eine Entität kann nur dann wieder angehängt werden, wenn es kein anderes JVM-Objekt (das derselben Datenbankzeile entspricht) gibt, das bereits mit der aktuellen Hibernate-Sitzung verbunden ist.

  • Zusammenführung

    Bei der Zusammenführung wird der Zustand der abgetrennten Entität (Quelle) in eine verwaltete Entitätsinstanz (Ziel) kopiert. Wenn die zusammenführende Entität keine Entsprechung in der aktuellen Sitzung hat, wird eine aus der Datenbank geholt.

    Die losgelöste Objektinstanz bleibt auch nach dem Zusammenführungsvorgang losgelöst.

  • entfernen

    Obwohl JPA vorschreibt, dass nur verwaltete Entitäten entfernt werden dürfen, kann Hibernate auch losgelöste Entitäten löschen (allerdings nur durch einen Aufruf der Methode Session#delete).

    Eine entfernte Entität wird nur zur Löschung eingeplant und die eigentliche DELETE-Anweisung der Datenbank wird während des Session Flush ausgeführt.

0 Stimmen

Es gibt also keine Möglichkeit, die Reihenfolge der Vorgänge bei orphanremoval=true?

0 Stimmen

Ihr Artikel über die Betriebsordnung im Normalfall. Meine Frage speziell für orphanRemoval

0 Stimmen

Überprüfen Sie meine Antwort . Es gibt nichts Magisches, was Hibernate hier tun sollte. Sie müssen nur den richtigen Code für die Datenzugriffslogik schreiben.

38voto

Sarah Vessels Punkte 29404

Ich habe festgestellt, dass ich bei der Verwendung von em.merge Ich habe eine SELECT Anweisung für jede INSERT selbst 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 gerade INSERT Aussagen dann.

4 Stimmen

Das ist sinnvoll, da Sie die IDs zuweisen und der JPA-Container keine Ahnung hat, woher Sie diese haben. Es besteht eine (kleine) Chance, dass das Objekt bereits in der Datenbank vorhanden ist, z. B. in einem Szenario, in dem mehrere Anwendungen in dieselbe Datenbank schreiben.

0 Stimmen

Ich hatte ein ähnliches Problem mit merge() . Ich hatte PostgreSQL-Datenbank mit komplizierten siehe Die Ansicht aggregierte Daten aus mehreren Tabellen (die Tabellen hatten eine identische Struktur, aber unterschiedliche Namen). Also versuchte JPA zu tun merge() aber eigentlich hat JPA erst einmal SELECT (Datenbank konnte aufgrund von View-Einstellungen mehrere Datensätze mit demselben Primärschlüssel aus verschiedenen Tabellen zurückgeben!), dann schlug JPA (Hibernate 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.

29voto

Raedwald Punkte 43209

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

Si X ein losgelöstes Objekt ist, wird die EntityExistsException kann ausgelöst werden, wenn die persistente Operation aufgerufen wird, oder die EntityExistsException oder eine andere PersistenceException kann beim Flush- oder Commit-Zeitpunkt geworfen werden.

Also mit persist() wäre geeignet, wenn das Objekt sollte nicht ein losgelöstes Objekt zu sein. Sie könnten es vorziehen, dass der Code die PersistenceException so dass es schnell scheitert.

Obwohl die Spezifikation ist unklar , persist() könnte die @GeneratedValue @Id für ein Objekt. merge() muss jedoch ein Objekt mit der Eigenschaft @Id bereits erzeugt.

5 Stimmen

+1 für " merge() muss jedoch ein Objekt mit der Eigenschaft @Id bereits erzeugt . ". Wann immer der EntityManager keinen Wert für das Feld der Objekt-ID findet, wird dieser in die DB persistiert (eingefügt).

0 Stimmen

Ich habe das zuerst nicht verstanden, weil ich mir über die Staaten nicht im Klaren war. Ich hoffe, das hilft jemandem, so wie es bei mir der Fall war. docs.jboss.org/hibernate/core/3.6/reference/de-US/html/

1 Stimmen

@GeneratedValue hat keine andere Auswirkung auf merge() und persist()

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X