Hibernate: save, persist, update, merge, saveOrUpdate

Einführung

In diesem Artikel werden wir die Unterschiede zwischen verschiedenen Methoden der Sitzungsschnittstelle diskutieren: save, persist, update, merge, saveOrUpdate.

Dies ist keine Einführung in Hibernate und Sie sollten bereits die Grundlagen der Konfiguration, der objektrelationalen Zuordnung und der Arbeit mit Entitätsinstanzen kennen. Einen Einführungsartikel zu Hibernate finden Sie in unserem Tutorial zu Hibernate 4 mit Spring.

Weiterführende Literatur:

Löschen von Objekten mit Hibernate

Kurzanleitung zum Löschen einer Entität in Hibernate.
Lesen Sie mehr →

Gespeicherte Prozeduren mit Ruhezustand

In diesem Artikel wird kurz erläutert, wie Speicherprozeduren aus dem Ruhezustand aufgerufen werden.
Lesen Sie mehr →

Eine Übersicht über Bezeichner in Hibernate / JPA

Erfahren Sie, wie Sie Entitätsbezeichner mit Hibernate zuordnen.
Lesen Sie mehr →

Session als Persistenzkontext Implementierung

Die Sitzungsschnittstelle verfügt über mehrere Methoden, die schließlich zum Speichern von Daten in der Datenbank führen: persist, save, update, merge, saveOrUpdate . Um den Unterschied zwischen diesen Methoden zu verstehen, müssen wir zuerst den Zweck der Sitzung als Persistenzkontext und den Unterschied zwischen den Zuständen von Entitätsinstanzen in Bezug auf die Sitzung diskutieren.

Wir sollten auch die Geschichte der Hibernate-Entwicklung verstehen, die zu einigen teilweise duplizierten API-Methoden führte.

2.1. Verwalten von Entitätsinstanzen

Abgesehen von der objektrelationalen Zuordnung selbst ist eines der Probleme, die Hibernate lösen sollte, das Problem der Verwaltung von Entitäten zur Laufzeit. Der Begriff „Persistenzkontext“ ist die Lösung von Hibernate für dieses Problem. Der Persistenzkontext kann als Container oder Cache der ersten Ebene für alle Objekte betrachtet werden, die Sie während einer Sitzung in eine Datenbank geladen oder gespeichert haben.

Die Sitzung ist eine logische Transaktion, deren Grenzen durch die Geschäftslogik Ihrer Anwendung definiert werden. Wenn Sie mit der Datenbank über einen Persistenzkontext arbeiten und alle Ihre Entitätsinstanzen an diesen Kontext angehängt sind, sollten Sie immer eine einzelne Instanz von entity für jeden Datenbankdatensatz haben, mit dem Sie während der Sitzung interagiert haben.

Im Ruhezustand wird der Persistenzkontext durch org dargestellt.Ruhezustand.Sitzungsinstanz. Für JPA ist es das Javax.Persistenz.EntityManager. Wenn wir Hibernate als JPA-Anbieter verwenden und über die EntityManager-Schnittstelle arbeiten, umschließt die Implementierung dieser Schnittstelle im Wesentlichen das zugrunde liegende Sitzungsobjekt. Hibernate Session bietet jedoch eine reichhaltigere Benutzeroberfläche mit mehr Möglichkeiten, so dass es manchmal nützlich ist, direkt mit Session zu arbeiten.

2.2. Zustände von Entitätsinstanzen

Jede Entitätsinstanz in Ihrer Anwendung wird in Bezug auf den Sitzungspersistenzkontext in einem der drei Hauptzustände angezeigt:

  • diese Instanz hat keine entsprechenden Zeilen in der Datenbank; Es ist normalerweise nur ein neues Objekt, das Sie erstellt haben, um es in der Datenbank zu speichern;
  • persistent – Diese Instanz ist einem eindeutigen Sitzungsobjekt zugeordnet; Beim Löschen der Sitzung in die Datenbank hat diese Entität garantiert einen entsprechenden konsistenten Datensatz in der Datenbank;
  • detached — Diese Instanz war einmal an eine Sitzung angehängt (in einem persistenten Zustand), ist es aber jetzt nicht; Eine Instanz tritt in diesen Zustand ein, wenn Sie sie aus dem Kontext entfernen, die Sitzung löschen oder schließen oder die Instanz einem Serialisierungs- / Deserialisierungsprozess unterziehen.

Hier ist ein vereinfachtes Zustandsdiagramm mit Kommentaren zu Sitzungsmethoden, die die Zustandsübergänge ermöglichen.

2016-07-11_13-38-11

Wenn sich die Entitätsinstanz im persistenten Zustand befindet, werden alle Änderungen, die Sie an den zugeordneten Feldern dieser Instanz vornehmen, beim Leeren der Sitzung auf die entsprechenden Datenbankeinträge und -felder angewendet. Die persistente Instanz kann als „online“ betrachtet werden, während die abgetrennte Instanz „offline“ gegangen ist und nicht auf Änderungen überwacht wird.

Dies bedeutet, dass Sie beim Ändern von Feldern eines persistenten Objekts nicht save , update oder eine dieser Methoden aufrufen müssen, um diese Änderungen an der Datenbank vorzunehmen: Sie müssen lediglich die Transaktion festschreiben oder die Sitzung leeren oder schließen, wenn Sie damit fertig sind.

2.3. Konformität mit der JPA-Spezifikation

Hibernate war die erfolgreichste Java ORM-Implementierung. Kein Wunder, dass die Spezifikation für die Java Persistence API (JPA) stark von der Hibernate API beeinflusst wurde. Leider gab es auch viele Unterschiede: einige große, einige subtilere.

Um als Implementierung des JPA-Standards zu fungieren, mussten Hibernate-APIs überarbeitet werden. Der Sitzungsschnittstelle wurden mehrere Methoden hinzugefügt, um der EntityManager-Schnittstelle zu entsprechen. Diese Methoden dienen dem gleichen Zweck wie die „ursprünglichen“ Methoden, entsprechen jedoch der Spezifikation und weisen daher einige Unterschiede auf.

Unterschiede zwischen den Operationen

Es ist wichtig, von Anfang an zu verstehen, dass alle Methoden (persist, save, update, merge, saveOrUpdate) nicht sofort zu den entsprechenden SQL UPDATE- oder INSERT-Anweisungen führen. Das tatsächliche Speichern von Daten in der Datenbank erfolgt beim Festschreiben der Transaktion oder beim Leeren der Sitzung.

Die genannten Methoden verwalten grundsätzlich den Status von Entitätsinstanzen, indem Sie sie entlang des Lebenszyklus zwischen verschiedenen Zuständen wechseln.

Als Beispiel-Entität verwenden wir eine einfache Annotations-gemappte Entität.:

@Entitypublic class Person { @Id @GeneratedValue private Long id; private String name; // ... getters and setters}

3.1. Persist

Die persist-Methode dient zum Hinzufügen einer neuen Entitätsinstanz zum Persistenzkontext, d. H. Zum Übergang einer Instanz vom transienten in den persistenten Zustand.

Normalerweise rufen wir es auf, wenn wir der Datenbank einen Datensatz hinzufügen möchten (eine Entitätsinstanz beibehalten):

Person person = new Person();person.setName("John");session.persist(person);

Was passiert, nachdem die persist-Methode aufgerufen wurde? Das person-Objekt ist vom transienten in den persistenten Zustand übergegangen. Das Objekt befindet sich jetzt im Persistenzkontext, ist jedoch noch nicht in der Datenbank gespeichert. Die Generierung von INSERT-Anweisungen erfolgt erst nach dem Festschreiben der Transaktion, dem Leeren oder Schließen der Sitzung.

Beachten Sie, dass die persist-Methode den Rückgabetyp void hat. Es arbeitet mit dem übergebenen Objekt „an Ort und Stelle“ und ändert seinen Zustand. Die variable person verweist auf das tatsächlich persistente Objekt.

Diese Methode ist eine spätere Ergänzung der Sitzungsschnittstelle. Das Hauptunterscheidungsmerkmal dieser Methode ist, dass sie der JSR-220-Spezifikation (EJB-Persistenz) entspricht. Die Semantik dieser Methode ist streng in der Spezifikation definiert, die im Grunde besagt, dass:

  • Eine transiente Instanz wird persistent (und die Operation kaskadiert alle ihre Beziehungen mit cascade=PERSIST oder cascade=ALL),
  • Wenn eine Instanz bereits persistent ist, hat dieser Aufruf keine Auswirkung auf diese bestimmte Instanz (aber er kaskadiert immer noch seine Beziehungen mit cascade=PERSIST oder cascade=ALL),
  • Wenn eine Instanz getrennt ist, sollten Sie eine Ausnahme erwarten, entweder beim Aufruf dieser Methode oder beim Festschreiben oder Leeren der Sitzung.

Beachten Sie, dass hier nichts den Bezeichner einer Instanz betrifft. Die Spezifikation gibt nicht an, dass die ID unabhängig von der ID-Generierungsstrategie sofort generiert wird. Die Spezifikation für die persist-Methode ermöglicht es der Implementierung, Anweisungen zum Generieren von id beim Commit oder flush auszugeben, und die ID ist nach dem Aufruf dieser Methode nicht garantiert ungleich null.

Sie können diese Methode für eine bereits persistente Instanz aufrufen, und es passiert nichts. Wenn Sie jedoch versuchen, eine getrennte Instanz beizubehalten, muss die Implementierung eine Ausnahme auslösen. Im folgenden Beispiel behalten wir die Entität bei, entfernen sie aus dem Kontext, damit sie getrennt wird, und versuchen dann erneut, sie beizubehalten. Der zweite Aufruf zur Sitzung.persist() verursacht eine Ausnahme, sodass der folgende Code nicht funktioniert:

Person person = new Person();person.setName("John");session.persist(person);session.evict(person);session.persist(person); // PersistenceException!

3.2. Save

Die save-Methode ist eine „ursprüngliche“ Hibernate-Methode, die nicht der JPA-Spezifikation entspricht.

Sein Zweck ist im Grunde der gleiche wie persist , aber es hat unterschiedliche Implementierungsdetails. Die Dokumentation für diese Methode besagt strikt, dass die Instanz beibehalten wird, indem „zuerst ein generierter Bezeichner zugewiesen wird“. Die Methode gibt garantiert den serialisierbaren Wert dieses Bezeichners zurück.

Person person = new Person();person.setName("John");Long id = (Long) session.save(person);

Der Effekt des Speicherns einer bereits persistierten Instanz ist derselbe wie bei persist. Der Unterschied tritt auf, wenn Sie versuchen, eine freistehende Instanz zu speichern:

Person person = new Person();person.setName("John");Long id1 = (Long) session.save(person);session.evict(person);Long id2 = (Long) session.save(person);

Die Variable id2 unterscheidet sich von id1. Der Aufruf von save on a detached instance erstellt eine neue persistente Instanz und weist ihr einen neuen Bezeichner zu, der beim Festschreiben oder Leeren zu einem doppelten Datensatz in einer Datenbank führt.

3.3. Merge

Die Hauptabsicht der Merge-Methode besteht darin, eine persistente Entitätsinstanz mit neuen Feldwerten aus einer getrennten Entitätsinstanz zu aktualisieren.

Angenommen, Sie haben eine RESTful-Schnittstelle mit einer Methode zum Abrufen eines JSON-serialisierten Objekts anhand seiner ID an den Aufrufer und einer Methode, die eine aktualisierte Version dieses Objekts vom Aufrufer empfängt. Eine Entität, die eine solche Serialisierung / Deserialisierung durchlaufen hat, wird in einem getrennten Zustand angezeigt.

Nach dem Deserialisieren dieser Entitätsinstanz müssen Sie eine persistente Entitätsinstanz aus einem Persistenzkontext abrufen und ihre Felder mit neuen Werten aus dieser getrennten Instanz aktualisieren. Die Merge-Methode macht also genau das:

  • findet eine Entitätsinstanz anhand der ID aus dem übergebenen Objekt (entweder wird eine vorhandene Entitätsinstanz aus dem Persistenzkontext abgerufen oder eine neue Instanz aus der Datenbank geladen);
  • kopiert Felder aus dem übergebenen Objekt in diese Instanz;
  • gibt eine neu aktualisierte Instanz zurück.

Im folgenden Beispiel entfernen (trennen) wir die gespeicherte Entität aus dem Kontext, ändern das Namensfeld und führen dann die getrennte Entität zusammen.

Person person = new Person(); person.setName("John"); session.save(person);session.evict(person);person.setName("Mary");Person mergedPerson = (Person) session.merge(person);

Beachten Sie, dass die merge—Methode ein Objekt zurückgibt – es ist das mergedPerson-Objekt, das in den Persistenzkontext geladen und aktualisiert wurde, nicht das person-Objekt, das Sie als Argument übergeben haben. Dies sind zwei verschiedene Objekte, und das person Objekt muss normalerweise verworfen werden (rechnen Sie nicht damit, dass es an den Persistenzkontext angehängt wird).

Wie bei der Persist-Methode wird die Merge-Methode von JSR-220 angegeben, um eine bestimmte Semantik zu haben, auf die Sie sich verlassen können:

  • wenn die Entität getrennt ist, wird sie auf eine vorhandene persistente Entität kopiert;
  • Wenn die Entität vorübergehend ist, wird sie auf eine neu erstellte persistente Entität kopiert;
  • Diese Operation kaskadiert für alle Beziehungen mit cascade=MERGE oder cascade=ALL Mapping;
  • Wenn die Entität persistent ist, hat dieser Methodenaufruf keine Auswirkungen darauf (die Kaskadierung findet jedoch weiterhin statt).

3.4. Update

Wie bei persist und save ist die Update-Methode eine „ursprüngliche“ Hibernate-Methode, die lange vor dem Hinzufügen der Merge-Methode vorhanden war. Seine Semantik unterscheidet sich in mehreren wichtigen Punkten:

  • es wirkt auf das übergebene Objekt (sein Rückgabetyp ist void); Die Update-Methode überführt das übergebene Objekt vom getrennten in den persistenten Zustand;
  • Diese Methode löst eine Ausnahme aus, wenn Sie eine transiente Entität übergeben.

Im folgenden Beispiel speichern wir das Objekt, entfernen es aus dem Kontext, ändern seinen Namen und rufen update auf. Beachten Sie, dass wir das Ergebnis der Aktualisierungsoperation nicht in eine separate Variable einfügen, da die Aktualisierung für das person Objekt selbst erfolgt. Grundsätzlich hängen wir die vorhandene Entitätsinstanz wieder an den Persistenzkontext an — etwas, was uns die JPA-Spezifikation nicht erlaubt.

Person person = new Person();person.setName("John");session.save(person);session.evict(person);person.setName("Mary");session.update(person);

Der Versuch, update für eine transiente Instanz aufzurufen, führt zu einer Ausnahme. Folgendes wird nicht funktionieren:

Person person = new Person();person.setName("John");session.update(person); // PersistenceException!

3.5. saveOrUpdate

Diese Methode wird nur in der Hibernate-API angezeigt und hat kein standardisiertes Gegenstück. Ähnlich wie update kann es auch zum erneuten Anhängen von Instanzen verwendet werden.

Tatsächlich ist die interne DefaultUpdateEventListener-Klasse, die die update-Methode verarbeitet, eine Unterklasse von DefaultSaveOrUpdateListener, die nur einige Funktionen überschreibt. Der Hauptunterschied der saveOrUpdate-Methode besteht darin, dass sie keine Ausnahme auslöst, wenn sie auf eine vorübergehende Instanz angewendet wird. Der folgende Code wird eine neu erstellte Instanz von Person beibehalten:

Person person = new Person();person.setName("John");session.saveOrUpdate(person);

Sie können sich diese Methode als universelles Werkzeug vorstellen, um ein Objekt persistent zu machen, unabhängig von seinem Zustand, ob es vorübergehend oder getrennt ist.

Was ist zu verwenden?

Wenn Sie keine besonderen Anforderungen haben, sollten Sie sich als Faustregel an die Persist- und Merge-Methoden halten, da diese standardisiert sind und garantiert der JPA-Spezifikation entsprechen.

Sie sind auch portabel, falls Sie sich entscheiden, zu einem anderen Persistenzanbieter zu wechseln, aber sie erscheinen manchmal nicht so nützlich wie die „ursprünglichen“ Hibernate-Methoden save, update und saveOrUpdate.

Fazit

Wir haben den Zweck verschiedener Hibernate-Sitzungsmethoden in Bezug auf die Verwaltung persistenter Entitäten zur Laufzeit erörtert. Wir haben gelernt, wie diese Methoden Entitätsinstanzen durch ihre Lebenszyklen durchlaufen und warum einige dieser Methoden doppelte Funktionen haben.



+