Entity Lifecycle im Ruhezustand

1. Übersicht

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.

In einem unserer vorherigen Tutorials erfahren Sie mehr über verschiedene Hibernate-Methoden, die sich mit Entitäten befassen.

2. Hilfsmethoden

In diesem Tutorial werden wir konsequent verschiedene Hilfsmethoden verwenden:

  • HibernateLifecycleUtil.getManagedEntities (Sitzung) - Wir verwenden es, um alle verwalteten Entitäten aus dem internen Speicher einer Sitzung abzurufen
  • DirtyDataInspector.getDirtyEntities () - Mit dieser Methode erhalten wir eine Liste aller Entitäten, die als "schmutzig" markiert wurden.
  • HibernateLifecycleUtil.queryCount (Abfrage) - eine bequeme Methode, um eine Abfrage (*) 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. Es geht nur um den Persistenzkontext

Bevor wir uns mit dem Thema Entity Lifecycle befassen, müssen wir zunächst den Persistenzkontext verstehen .

Einfach ausgedrückt befindet sich der Persistenzkontext zwischen Clientcode und Datenspeicher . Es ist ein Staging-Bereich, in dem persistente Daten in Entitäten konvertiert werden, die vom Clientcode gelesen und geändert werden können.

Theoretisch ist der Persistenzkontext eine Implementierung des Musters der Arbeitseinheit. Es verfolgt alle geladenen Daten, verfolgt Änderungen dieser Daten und ist dafür verantwortlich, eventuelle Änderungen am Ende des Geschäftsvorfalls wieder mit der Datenbank zu synchronisieren.

JPA EntityManager und Hibernate's Session sind eine Implementierung des Persistenzkontextkonzepts . In diesem Artikel werden wir Hibernate Session darstellen Persistenzkontext.

Der Status des Ruhezustands der Entität im Ruhezustand erklärt, wie die Entität mit einem Persistenzkontext zusammenhängt , wie wir als Nächstes sehen werden.

4. Verwaltete Entität

Eine verwaltete Entität ist eine Darstellung einer Datenbanktabellenzeile (obwohl diese Zeile noch nicht in der Datenbank vorhanden sein muss).

Dies wird von der aktuell ausgeführten Sitzung verwaltet , und jede daran vorgenommene Änderung wird automatisch verfolgt und an die Datenbank weitergegeben .

Die Sitzung lädt entweder die Entität aus der Datenbank oder fügt eine getrennte Entität erneut hinzu. Wir werden getrennte Entitäten in Abschnitt 5 diskutieren.

Lassen Sie uns einen Code beobachten, um eine Klarstellung zu erhalten.

Unsere Beispielanwendung definiert eine Entität, die FootballPlayer- Klasse. 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 Namen Gianluigi Buffon anstelle von Gigi Buffon eingeben.

Zunächst müssen wir unsere Arbeitseinheit mit einer Sitzung beginnen:

Session session = sessionFactory.openSession();

In einer Serverumgebung können wir eine Sitzung über einen kontextsensitiven Proxy in unseren Code einfügen. Das Prinzip bleibt dasselbe: Wir brauchen eine Sitzung , um den Geschäftsvorgang unserer Arbeitseinheit zusammenzufassen.

Als Nächstes weisen wir unsere Sitzung an, 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 eine Sitzung zum ersten Mal erhalten , ist ihr persistenter Kontextspeicher leer, wie unsere erste Assert- 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ückgibt, die wir verwenden können.

Intern verfolgt die Sitzung alle Entitäten, die sie im persistenten Kontextspeicher lädt. In unserem Fall enthält der interne Speicher der Sitzung nach der Abfrage drei Entitäten.

Jetzt ändern wir Gigis Namen:

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 Transaktion commit () oder flush () findet die Sitzung alle fehlerhaften Entitäten aus ihrer Verfolgungsliste und synchronisiert den Status mit der Datenbank.

Beachten Sie, dass wir keine Methode aufrufen mussten, um Session zu benachrichtigen, 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, dh die INSERT-Anweisung steht noch aus, bis die Arbeitseinheit beendet ist.

Siehe das Kapitel über vorübergehende Entitäten weiter unten.

5. Freistehende Entität

Eine getrennte Entität ist nur eine gewöhnliche Entität POJO, deren Identitätswert einer Datenbankzeile entspricht. Der Unterschied zu einer verwalteten Entität besteht darin, dass sie von keinem Persistenzkontext mehr verfolgt wird .

Eine Entität kann getrennt werden, wenn die zum Laden verwendete Sitzung geschlossen wurde oder wenn Session.evict (Entität) oder Session.clear () aufgerufen wird .

Lassen Sie es uns im Code sehen:

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 in getrennten Entitäten nicht verfolgt:

cr7.setName("CR7"); transaction.commit(); assertThat(getDirtyEntities()).isEmpty();

Session.merge (Entität) /Session.update (Entität) kann eine Sitzung (erneut) 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");

Referenz zu Session.merge () und Session.update () finden Sie hier.

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 der persistenten Daten entspricht, die zu Gigi Buffon gehören. Das Aufrufen von update () hat genau den gleichen Effekt, als hätten wir die Entität aus einem anderen Persistenzkontext geladen .

Tatsächlich unterscheidet Session nicht, woher eine neu angehängte Entität stammt.

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

In Bezug auf die Sitzung ist eine getrennte Entität nur eine einfache Entität, deren Identitätswert persistenten Daten entspricht.

Be aware that the example above just serves a demo purpose. and we need to know exactly what we're doing. Otherwise, we could end up with null values across our entity if we just set the value on the field we want to update, leaving the rest untouched (so, effectively null).

6. Transient Entity

A transient entity is simply an entity object that has no representation in the persistent store and is not managed by any Session.

A typical example of a transient entity would be instantiating a new entity via its constructor.

To make a transient entity persistent, we need to call Session.save(entity) or Session.saveOrUpdate(entity):

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);

As soon as we execute Session.save(entity), the entity is assigned an identity value and becomes managed by the Session. However, it might not yet be available in the database as the INSERT operation might be delayed until the end of the unit of work.

7. Deleted Entity

An entity is in a deleted (removed) state if Session.delete(entity) has been called, and the Session has marked the entity for deletion. The DELETE command itself might be issued at the end of the unit of work.

Let's see it in the following code:

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

However, notice that the entity stays in the persistent context store until the end of the unit of work.

8. Conclusion

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

Der in diesem Artikel verwendete Code befindet sich wie üblich auf GitHub.