Hibernate-Lebenszyklus

Lebenszyklus von Entitäten im Ruhezustand

1. Überblick

Jede Entität im Ruhezustand hat natürlich einen Lebenszyklus innerhalb des Frameworks - entweder in einem vorübergehenden, verwalteten, getrennten oder gelöschten Zustand.

Das Verständnis dieser Zustände sowohl auf konzeptioneller als auch auf technischer Ebene ist wichtig, um Hibernate ordnungsgemäß verwenden zu können.

Weitere Informationen zu verschiedenen Hibernate-Methoden, die sich mit Entitäten befassen, finden Sie unterone of our previous tutorials.

2. Hilfsmethoden

In diesem Tutorial werden wir konsequent verschiedene Hilfsmethoden verwenden:

  • HibernateLifecycleUtil.getManagedEntities(session) – Wir verwenden es, um alle verwalteten Entitäten aus dem internen Speicher einesSession’sabzurufen

  • DirtyDataInspector.getDirtyEntities() – Mit dieser Methode erhalten wir eine Liste aller Entitäten, die als "schmutzig" markiert wurden.

  • HibernateLifecycleUtil.queryCount(query) – ist eine bequeme Methode, umcount(*) Abfragen für die eingebettete Datenbank durchzuführen

Alle oben genannten Hilfsmethoden werden zur besseren Lesbarkeit statisch importiert. Sie finden ihre Implementierungen im GitHub-Projekt, das am Ende dieses Artikels verlinkt ist.

3. Alles dreht sich um den Persistenzkontext

Bevor wir uns mit dem Thema Entity Lifecycle befassen, müssen wir zunächstthe persistence context verstehen.

Simply put, the persistence context sits between client code and data store. In diesem Staging-Bereich werden persistente Daten in Entitäten konvertiert, die vom Clientcode gelesen und geändert werden können.

Theoretisch istpersistence context eine Implementierung desUnit of Work-Musters. Es verfolgt alle geladenen Daten, verfolgt Änderungen dieser Daten und ist dafür verantwortlich, alle Änderungen am Ende des Geschäftsvorgangs wieder mit der Datenbank zu synchronisieren.

JPAEntityManager und HibernateSession ind eine Implementierung despersistence context -Sconcept. In diesem Artikel verwenden wir HibernateSession, umpersistence context. darzustellen

Der Status des Entitätslebenszyklus im Ruhezustand erklärt, wie die Entität mit einempersistence context zusammenhängt, wie wir als Nächstes sehen werden.

4. Verwaltete Entität

A managed entity is a representation of a database table row (obwohl diese Zeile noch nicht in der Datenbank vorhanden sein muss).

Dies wird von den aktuell ausgeführtenSession undevery change made on it will be tracked and propagated to the database automatically verwaltet.

MitSession wird entweder die Entität aus der Datenbank geladen oder eine getrennte Entität erneut angehängt. Wir werden getrennte Entitäten in Abschnitt 5 diskutieren.

Sehen wir uns einen Code an, um Klarheit zu erhalten.

Unsere Beispielanwendung definiert eine Entität, die KlasseFootballPlayer. Beim Start initialisieren wir den Datenspeicher mit einigen Beispieldaten:

+-------------------+-------+
| Name              |  ID   |
+-------------------+-------+
| Cristiano Ronaldo | 1     |
| Lionel Messi      | 2     |
| Gianluigi Buffon  | 3     |
+-------------------+-------+

Nehmen wir an, wir möchten zunächst den Namen von Buffon ändern - wir möchten seinen vollständigen NamenGianluigi Buffon anstelle von Gigi Buffon eingeben.

Zuerst müssen wir unsere Arbeitseinheit beginnen, indem wirSession: erhalten

Session session = sessionFactory.openSession();

In einer Serverumgebung können wir über einen kontextsensitiven Proxy einSession in unseren Code einfügen. Das Prinzip bleibt das gleiche: Wir brauchen einSession , um den Geschäftsvorgang unserer Arbeitseinheit zu kapseln.

Als Nächstes weisen wir unsereSessionan, die Daten aus dem persistenten Speicher zu laden:

assertThat(getManagedEntities(session)).isEmpty();

List players = s.createQuery("from FootballPlayer").getResultList();

assertThat(getManagedEntities(session)).size().isEqualTo(3);

Wenn wir zum ersten Mal einSession erhalten, ist sein persistenter Kontextspeicher leer, wie unsere ersteassert-Anweisung zeigt.

Als Nächstes führen wir eine Abfrage aus, die Daten aus der Datenbank abruft, eine Entitätsdarstellung der Daten erstellt und schließlich die Entität zur Verwendung zurückgibt.

Intern verzerrtSession die Verfolgung aller Entitäten, die im persistenten Kontextspeicher geladen werden. In unserem Fall enthält der sinternale SpeicherSession’s nach der Abfrage 3 Entitäten.

Jetzt ändern wir den Namen von Gigi:

Transaction transaction = session.getTransaction();
transaction.begin();

FootballPlayer gigiBuffon = players.stream()
  .filter(p -> p.getId() == 3)
  .findFirst()
  .get();

gigiBuffon.setName("Gianluigi Buffon");
transaction.commit();

assertThat(getDirtyEntities()).size().isEqualTo(1);
assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Gianluigi Buffon");

4.1. Wie funktioniert es?

Beim Aufruf der Transaktioncommit() oderflush() finden dieSession alledirty-Entitäten aus ihrer Verfolgungsliste und synchronisieren den Status mit der Datenbank.

Beachten Sie, dass wir keine Methode aufrufen mussten, umSession darüber zu informieren, dass wir etwas in unserer Entität geändert haben. Da es sich um eine verwaltete Entität handelt, werden alle Änderungen automatisch an die Datenbank weitergegeben.

Eine verwaltete Entität ist immer eine persistente Entität - sie muss eine Datenbankkennung haben, obwohl die Datenbankzeilendarstellung noch nicht erstellt wurde, d. H. Die INSERT-Anweisung wartet auf das Ende der Arbeitseinheit.

Siehe das Kapitel über transiente Entitäten weiter unten.

5. Freistehende Einheit

detached entity is just an ordinary entity POJO, deren Identitätswert einer Datenbankzeile entspricht. Der Unterschied zu einer verwalteten Entität besteht darin, dass es sich umnot tracked anymore by any persistence contexthandelt.

Eine Entität kann sich lösen, wenn die zum Laden verwendetenSession geschlossen wurden oder wenn wirSession.evict(entity) oderSession.clear() aufrufen.

Sehen wir es uns im Code an:

FootballPlayer cr7 = session.get(FootballPlayer.class, 1L);

assertThat(getManagedEntities(session)).size().isEqualTo(1);
assertThat(getManagedEntities(session).get(0).getId()).isEqualTo(cr7.getId());

session.evict(cr7);

assertThat(getManagedEntities(session)).size().isEqualTo(0);

In unserem Persistenzkontext werden die Änderungen an getrennten Entitäten nicht verfolgt:

cr7.setName("CR7");
transaction.commit();

assertThat(getDirtyEntities()).isEmpty();

Session.merge(entity)/Session.update(entity) can  (erneut) eine Sitzung anhängen:

FootballPlayer messi = session.get(FootballPlayer.class, 2L);

session.evict(messi);
messi.setName("Leo Messi");
transaction.commit();

assertThat(getDirtyEntities()).isEmpty();

transaction = startTransaction(session);
session.update(messi);
transaction.commit();

assertThat(getDirtyEntities()).size().isEqualTo(1);
assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Leo Messi");

Als Referenz fürSession.merge() undSession.update() siehehere.

5.1. Das Identitätsfeld ist alles, was zählt

Schauen wir uns die folgende Logik an:

FootballPlayer gigi = new FootballPlayer();
gigi.setId(3);
gigi.setName("Gigi the Legend");
session.update(gigi);

Im obigen Beispiel haben wir eine Entität auf die übliche Weise über ihren Konstruktor instanziiert. Wir haben die Felder mit Werten gefüllt und die Identität auf 3 gesetzt, was der Identität persistenter Daten entspricht, die zu Gigi Buffon gehören. Das Aufrufen vonupdate() hat genau den gleichen Effekt, als hätten wir die Entität von einem anderenpersistence context geladen.

Tatsächlich unterscheidetSession nicht, woher eine wieder verbundene Entität stammt.

In Webanwendungen ist es ein weit verbreitetes Szenario, getrennte Entitäten aus HTML-Formularwerten zu erstellen.

FürSession ist eine getrennte Entität nur eine einfache Entität, deren Identitätswert persistenten Daten entspricht.

Beachten Sie, dass das obige Beispiel nur einem Demo-Zweck dient. und wir müssen genau wissen, was wir tun. Andernfalls könnten wir Nullwerte für unsere Entität erhalten, wenn wir nur den Wert für das zu aktualisierende Feld festlegen und den Rest unberührt lassen (also praktisch Null).

6. Vorübergehende Entität

A transient entity is simply an entity object that has no representation in the persistent store und wird von keinemSession verwaltet.

Ein typisches Beispiel für eine vorübergehende Entität wäre die Instanziierung einer neuen Entität über ihren Konstruktor.

Um eine transiente Entitätpersistent zu machen, müssen wirSession.save(entity) orSession.saveOrUpdate(entity): aufrufen

FootballPlayer neymar = new FootballPlayer();
neymar.setName("Neymar");
session.save(neymar);

assertThat(getManagedEntities(session)).size().isEqualTo(1);
assertThat(neymar.getId()).isNotNull();

int count = queryCount("select count(*) from Football_Player where name='Neymar'");

assertThat(count).isEqualTo(0);

transaction.commit();
count = queryCount("select count(*) from Football_Player where name='Neymar'");

assertThat(count).isEqualTo(1);

Sobald wirSession.save(entity) ausführen, wird der Entität ein Identitätswert zugewiesen und vonSession verwaltet. Es ist jedoch möglicherweise noch nicht in der Datenbank verfügbar, da die INSERT-Operation möglicherweise bis zum Ende der Arbeitseinheit verzögert wird.

7. Entität gelöscht

An entity is in a deleted (removed) state if Session.delete(entity) wurde aufgerufen, undSession hat die Entität zum Löschen markiert. Der Befehl DELETE selbst wird möglicherweise am Ende der Arbeitseinheit ausgegeben.

Sehen wir es uns im folgenden Code an:

session.delete(neymar);

assertThat(getManagedEntities(session).get(0).getStatus()).isEqualTo(Status.DELETED);

Beachten Sie jedoch, dass die Entität bis zum Ende der Arbeitseinheit im permanenten Kontextspeicher verbleibt.

8. Fazit

Das Konzept vonpersistence context ist von zentraler Bedeutung für das Verständnis des Lebenszyklus von Entitäten im Ruhezustand. Wir haben den Lebenszyklus anhand der Codebeispiele für jeden Status geklärt.

Wie üblich ist der in diesem Artikel verwendete Codeover on GitHub.