JPA Merge vs. Persist [Duplikat]

68

Bisher war es meine Präferenz, immer EntityManager zu verwenden merge(), um sowohl das Einfügen als auch das Aktualisieren zu erledigen. Ich habe jedoch auch festgestellt, dass beim Zusammenführen vor dem Aktualisieren / Einfügen zusätzliche Auswahlabfragen ausgeführt werden, um sicherzustellen, dass der Datensatz noch nicht in der Datenbank vorhanden ist.

Jetzt arbeite ich an einem Projekt, das umfangreiche (Massen-) Einfügungen in die Datenbank erfordert. Ist es aus Sicht der Leistung sinnvoll, in einem Szenario, in dem ich absolut weiß, dass ich immer eine neue Instanz von Objekten erstelle, die beibehalten werden sollen, persist anstelle von merge zu verwenden?

phewataal
quelle

Antworten:

71

Es ist keine gute Idee, mergewenn eine persistausreicht - mergemacht viel mehr Arbeit. Das Thema wurde auf Stackoverflow diskutiert vor, und dieser Artikel erläutert die Unterschiede im Detail, mit einigen netten Flussdiagramme , um die Dinge klar zu machen.

Óscar López
quelle
2
Diese "Antwort" entspricht dem Markieren der Frage als doppelt und dem Hinzufügen eines Links zum Artikel zu dieser oder der doppelten Frage.
Karl Richter
9

Ich würde auf jeden Fall mit beharrlich gehen, persist()wenn, wie Sie sagten:

(...) Ich weiß absolut, dass ich immer eine neue Instanz von Objekten erstelle, die beibehalten werden sollen (...)

Darum geht es bei dieser Methode - sie schützt Sie in Fällen, in denen die Entität bereits vorhanden ist (und setzt Ihre Transaktion zurück).

Piotr Nowicki
quelle
Ist es eine gute Codierungspraxis, etwas zu tun wie: try {em.persist (xxx); } catch (RuntimeException re) {try {xxx = em.merge (xxx); } catch (Ausnahme e) {throw e; }}
Phewataal
7
Trotzdem ist das Fangen RuntimeExceptiondefinitiv keine gute Praxis (aber ich nehme an, Sie haben es gemeint EntityExistsException). Ich würde es nicht als universelle gute Codierungspraxis bezeichnen . Das hängt von Ihren Anforderungen ab. Wenn die Anforderungen sagen , dass das Objekt muss beibehalten werden und es darf nicht vorhanden sein , bevor diese Aktion stattfindet - ich würde auf jeden Fall nicht versuchen merge zu tun , nachdem Ausnahme zu kontrollieren. Wenn Sie den JTA-Entitätsmanager verwenden, wird Ihre Transaktion zu diesem Zeitpunkt bereits für das Rollback markiert.
Piotr Nowicki
Können Sie bitte erklären, was Sie unter "Wenn Sie den JTA-Entitätsmanager verwenden, wird Ihre Transaktion zu diesem Zeitpunkt bereits für den Rollback markiert". Ich verwende CMT für EJB3 und konnte überprüfen, dass keine Transaktion zurückgesetzt wird, solange ich die Ausnahme innerhalb einer Methodengrenze esse.
Phewataal
2
Ich vermute, wenn Sie persisteine bereits vorhandene Entität aufrufen , erhalten Sie diese EntityExistsExceptionund aufgrund des JPA-Implementierers wird diese Transaktion zurückgesetzt (nicht, weil es sich um eine Laufzeitausnahme handelt). Mit anderen Worten - ich denke (nicht sicher), dass selbst wenn Sie diese Ausnahme versuchen / abfangen, der TX weiterhin für den Rollback markiert wird. Der mergewird niemals werfen EntityExistsException.
Piotr Nowicki
2

Wenn Sie den zugewiesenen Generator verwenden, kann die Verwendung von mergeanstelle von persisteine 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.

Zunächst müssen wir alle Entitätszustände definieren:

  • 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 dieser 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 .

  • Löste sich

    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 Session # delete-Methode). Eine entfernte Entität ist nur zum Löschen geplant, und die tatsächliche Datenbank-DELETE-Anweisung wird während der Sitzungsspülzeit ausgeführt.

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

Geben Sie hier die Bildbeschreibung ein

Oder wenn Sie die Hibernate-spezifische API verwenden:

Geben Sie hier die Bildbeschreibung ein

Vlad Mihalcea
quelle