Ruhezustand: speichern, beibehalten, aktualisieren, zusammenführen, speichernOrUpdate

Ruhezustand: speichern, beibehalten, aktualisieren, zusammenführen, saveOrUpdate

1. Einführung

In diesem Artikel werden wir die Unterschiede zwischen verschiedenen Methoden derSession-Schnittstelle diskutieren:save,persist,update,merge,saveOrUpdate.

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

Weitere Lektüre:

Objekte im Ruhezustand löschen

Kurzanleitung zum Löschen eines Objekts im Ruhezustand.

Read more

Gespeicherte Prozeduren mit Ruhezustand

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

Read more

Eine Übersicht der Bezeichner im Ruhezustand

Erfahren Sie, wie Sie Entitäts-IDs mit Hibernate zuordnen.

Read more

2. Sitzung als Persistenzkontextimplementierung

DieSession-Schnittstelle 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 vonSession als Persistenzkontext und den Unterschied zwischen den Zuständen von Entitätsinstanzen in Bezug aufSession diskutieren.

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

2.1. Entitätsinstanzen verwalten

Abgesehen von der objektrelationalen Zuordnung selbst bestand eines der Probleme, die Hibernate lösen sollte, darin, Entitäten zur Laufzeit zu verwalten. 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 dort gespeichert haben.

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

Im Ruhezustand wird der Persistenzkontext durch die Instanz vonorg.hibernate.Sessiondargestellt. Für JPA ist es dasjavax.persistence.EntityManager. Wenn wir Hibernate als JPA-Anbieter verwenden und über dieEntityManager-Schnittstelle arbeiten, umschließt die Implementierung dieser Schnittstelle im Wesentlichen das zugrunde liegendeSession-Objekt. HibernateSession bietet jedoch eine umfangreichere Schnittstelle mit mehr Möglichkeiten, sodass es manchmal nützlich ist, mitSession directly zu arbeiten.

2.2. Instanzen von Entitätszuständen

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

  • transient - Diese Instanz ist nicht anSession gebunden und wurde auch nie damit verbunden. Diese Instanz hat keine entsprechenden Zeilen in der Datenbank. Es handelt sich normalerweise nur um ein neues Objekt, das Sie zum Speichern in der Datenbank erstellt haben.

  • persistent - Diese Instanz ist einem eindeutigenSession-Objekt zugeordnet. Beim Leeren derSession in die Datenbank wird garantiert, dass diese Entität einen entsprechenden konsistenten Datensatz in der Datenbank hat.

  • detached - Diese Instanz wurde einmal an einSession angehängt (in einempersistent-Zustand), jetzt jedoch nicht mehr. Eine Instanz wechselt in diesen Status, 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 zuSession-Methoden, die die Zustandsübergänge ermöglichen.

2016-07-11_13-38-11

Wenn sich die Entitätsinstanz im Statuspersistentbefindet, werden alle Änderungen, die Sie an den zugeordneten Feldern dieser Instanz vornehmen, beim Leeren derSession auf die entsprechenden Datenbankeinträge und Felder angewendet. Die Instanz vonpersistentkann als "online" betrachtet werden, während die Instanz vondetached"offline" geschaltet wurde und nicht auf Änderungen überwacht wird.

Dies bedeutet, dass Sie beim Ändern der Felder einespersistent-Objekts nichtsave,update oder eine dieser Methoden aufrufen müssen, um diese Änderungen in die Datenbank zu übertragen: alles, was Sie benötigen besteht darin, die Transaktion festzuschreiben oder die Sitzung zu leeren oder zu 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 größere, andere subtilere.

Um als Implementierung des JPA-Standards zu fungieren, mussten die Hibernate-APIs überarbeitet werden. Die Sitzungsoberfläche wurde um mehrere Methoden erweitert, die mit der EntityManager-Oberfläche übereinstimmen. Diese Methoden dienen dem gleichen Zweck wie die "ursprünglichen" Methoden, entsprechen jedoch der Spezifikation und weisen daher einige Unterschiede auf.

3. Unterschiede zwischen den Operationen

Es ist wichtig von Anfang an zu verstehen, dass alle Methoden (persist,save,update,merge,saveOrUpdate) nicht sofort zu dem Ergebnis führen entsprechende SQLUPDATE- oderINSERT-Anweisungen. Das tatsächliche Speichern von Daten in der Datenbank erfolgt beim Festschreiben der Transaktion oder beim Löschen derSession.

Die genannten Methoden verwalten im Wesentlichen den Status von Entitätsinstanzen, indem sie diese über den Lebenszyklus zwischen verschiedenen Status wechseln.

Als Beispielentität verwenden wir eine einfache Entität mit AnnotationszuordnungPerson:

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    // ... getters and setters

}

3.1. Persist

Die Methodepersist dient zum Hinzufügen einer neuen Entitätsinstanz zum Persistenzkontext, d. H. Übergang einer Instanz vom vorübergehenden in den Zustand vonpersistent.

Wir nennen es normalerweise, 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 nach dem Aufruf der Methodepersist? Das Objektperson ist vom Zustandtransient in den Zustandpersistentübergegangen. Das Objekt befindet sich jetzt im Persistenzkontext, ist jedoch noch nicht in der Datenbank gespeichert. Die Generierung vonINSERT-Anweisungen erfolgt nur beim Festschreiben der Transaktion, beim Löschen oder Schließen der Sitzung.

Beachten Sie, dass die Methodepersistden Rückgabetypvoidhat. Es bearbeitet das übergebene Objekt „an Ort und Stelle“ und ändert seinen Zustand. Die Variablepersonverweist auf das tatsächlich persistierte 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 in der Spezifikation genau definiert, in der im Grunde gesagt wird, dass:

  • Einetransient-Instanz wird zupersistent (und die Operation kaskadiert zu allen ihren Beziehungen mitcascade=PERSIST odercascade=ALL).

  • Wenn eine Instanz bereitspersistent ist, hat dieser Aufruf keine Auswirkung auf diese bestimmte Instanz (aber er kaskadiert immer noch zu seinen Beziehungen mitcascade=PERSIST odercascade=ALL).

  • Wenn eine Instanzdetached ist, sollten Sie eine Ausnahme erwarten, entweder beim Aufrufen dieser Methode oder beim Festschreiben oder Löschen der Sitzung.

Beachten Sie, dass es hier nichts gibt, was den Bezeichner einer Instanz betrifft. Die Spezifikation gibt nicht an, dass die ID unabhängig von der ID-Generierungsstrategie sofort generiert wird. Die Angabe für die Methodepersistermöglicht es der Implementierung, Anweisungen zum Generieren einer ID beim Festschreiben oder Löschen auszugeben, und es wird nicht garantiert, dass die ID nach dem Aufrufen dieser Methode nicht null ist. Sie sollten sich daher nicht darauf verlassen.

Sie können diese Methode für eine bereitspersistent-Instanz aufrufen, und es passiert nichts. Wenn Sie jedoch versuchen, die Instanz vondetachedbeizubehalten, muss die Implementierung eine Ausnahme auslösen. Im folgenden Beispiel werden wirpersist die Entität,evict es aus dem Kontext, so dass esdetached wird, und versuchen dann erneut,persist. Der zweite Aufruf vonsession.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

Diesave-Methode ist eine "ursprüngliche" Hibernate-Methode, die nicht der JPA-Spezifikation entspricht.

Sein Zweck ist im Grunde der gleiche wiepersist, es gibt jedoch unterschiedliche Implementierungsdetails. In der Dokumentation zu dieser Methode wird strengstens angegeben, dass die Instanz beibehalten wird, indem zunächst ein generierter Bezeichner zugewiesen wird. Die Methode gibt garantiert denSerializable-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 der gleiche wie beipersist. Der Unterschied ergibt sich, wenn Sie versuchen, die Instanz vondetachedzu speichern:

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

session.evict(person);
Long id2 = (Long) session.save(person);

Die Variableid2 unterscheidet sich vonid1. Der Aufruf zum Speichern einerdetached-Instanz erstellt eine neuepersistent-Instanz und weist ihr eine neue Kennung zu, die beim Festschreiben oder Löschen zu einem doppelten Datensatz in einer Datenbank führt.

3.3. Merge

Die Hauptabsicht der Methodemergebesteht darin, eine Entitätsinstanz vonpersistentmit neuen Feldwerten aus einer Entitätsinstanz vondetachedzu 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 vom Aufrufer eine aktualisierte Version dieses Objekts empfängt. Eine Entität, die eine solche Serialisierung / Deserialisierung durchlaufen hat, wird im Statusdetachedangezeigt.

Nach dem Deserialisieren dieser Entitätsinstanz müssen Sie einepersistent-Entitätsinstanz aus einem Persistenzkontext abrufen und ihre Felder mit neuen Werten aus dieserdetached-Instanz aktualisieren. Die Methodemergemacht genau das:

  • findet eine Entitätsinstanz anhand der ID des übergebenen Objekts (entweder wird eine vorhandene Entitätsinstanz aus dem Persistenzkontext abgerufen oder eine neue Instanz aus der Datenbank geladen);

  • kopiert Felder vom übergebenen Objekt in diese Instanz;

  • Gibt eine neu aktualisierte Instanz zurück.

Im folgenden Beispiel wird die gespeicherte Entitätevictvom Kontext getrennt, das Feldnamegeändert und dannmergedie Entitätdetached.

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 Methodemerge ein Objekt zurückgibt - es ist das ObjektmergedPerson, das in den Persistenzkontext geladen und aktualisiert wurde, nicht das Objektperson, das Sie als Argument übergeben haben. Dies sind zwei verschiedene Objekte, und dasperson-Objekt muss normalerweise verworfen werden (rechnen Sie sowieso nicht damit, dass es an den Persistenzkontext angehängt wird).

Wie bei derpersist-Methode wird diemerge-Methode von JSR-220 angegeben, um bestimmte Semantiken zu erhalten, auf die Sie sich verlassen können:

  • Wenn die Entitätdetached ist, wird sie auf eine vorhandenepersistent-Entität kopiert.

  • Wenn die Entitättransient ist, wird sie auf eine neu erstelltepersistent-Entität kopiert.

  • Diese Operation kaskadiert für alle Beziehungen mitcascade=MERGE odercascade=ALL Mapping.

  • Wenn die Entitätpersistent ist, hat dieser Methodenaufruf keine Auswirkung darauf (die Kaskadierung findet jedoch weiterhin statt).

3.4. Update

Wie beipersist undsave ist dieupdate-Methode eine „ursprüngliche“ Hibernate-Methode, die lange vor dem Hinzufügen dermerge-Methode vorhanden war. Die Semantik unterscheidet sich in mehreren wichtigen Punkten:

  • es wirkt auf übergebenes Objekt (sein Rückgabetyp istvoid); Die Methodeupdate wechselt das übergebene Objekt vom Zustanddetached in den Zustandpersistent.

  • Diese Methode löst eine Ausnahme aus, wenn Sie einetransient-Entität übergeben.

Im folgenden Beispielsave das Objekt, dannevict (trennen) es aus dem Kontext, ändern dann seinename und rufenupdate auf. Beachten Sie, dass wir das Ergebnis der Operationupdatenicht in eine separate Variable einfügen, daupdate auf dem Objektpersonelbst stattfindet. Grundsätzlich ordnen wir die vorhandene Entitätsinstanz wieder dem Persistenzkontext zu - was die JPA-Spezifikation nicht zulässt.

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

person.setName("Mary");
session.update(person);

Der Versuch,update auf einertransient-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 wieupdate kann es auch zum erneuten Anhängen von Instanzen verwendet werden.

Tatsächlich ist die interneDefaultUpdateEventListener-Klasse, die dieupdate-Methode verarbeitet, eine Unterklasse vonDefaultSaveOrUpdateListener, die nur einige Funktionen überschreibt. Der Hauptunterschied der Methode vonsaveOrUpdatebesteht darin, dass sie keine Ausnahme auslöst, wenn sie auf eine Instanz vontransientangewendet wird. Stattdessen wird diesetransient-Instanzpersistent. Der folgende Code behält eine neu erstellte Instanz vonPerson bei:

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

Sie können sich diese Methode als universelles Werkzeug vorstellen, um ein Objektpersistent zu erstellen, unabhängig von seinem Zustand, ob estransient oderdetached ist.

4. Was ist zu verwenden?

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

Sie sind auch portabel, falls Sie sich für einen Wechsel zu einem anderen Persistenzanbieter entscheiden. Manchmal erscheinen sie jedoch nicht so nützlich wie die „ursprünglichen“ Hibernate-Methodensave,update undsaveOrUpdate.

5. Fazit

Wir haben den Zweck verschiedener Methoden für Ruhezustandsitzungen in Bezug auf die Verwaltung persistenter Entitäten zur Laufzeit erörtert. Wir haben gelernt, wie diese Methoden Entitätsinstanzen während ihres Lebenszyklus übertragen und warum einige dieser Methoden doppelte Funktionen haben.

Der Quellcode für den Artikel lautetavailable on GitHub.