JPA / Hibernate Persistence Context

1. Übersicht

Persistenzanbieter wie Hibernate verwenden den Persistenzkontext, um den Entitätslebenszyklus in einer Anwendung zu verwalten.

In diesem Tutorial beginnen wir mit der Einführung des Persistenzkontexts und sehen dann, warum dies wichtig ist. Schließlich werden wir anhand von Beispielen den Unterschied zwischen dem Persistenzkontext mit Transaktionsbereich und dem Persistenzkontext mit erweitertem Bereich sehen.

2. Persistenzkontext

Werfen wir einen Blick auf die offizielle Definition des Persistenzkontexts:

Eine EntityManager-Instanz ist einem Persistenzkontext zugeordnet. Ein Persistenzkontext ist eine Reihe von Entitätsinstanzen, in denen für jede persistente Entitätsidentität eine eindeutige Entitätsinstanz vorhanden ist. Im Persistenzkontext werden die Entitätsinstanzen und ihr Lebenszyklus verwaltet. Die EntityManager-API wird zum Erstellen und Entfernen persistenter Entitätsinstanzen, zum Suchen von Entitäten anhand ihres Primärschlüssels und zum Abfragen von Entitäten verwendet.

Die obige Aussage mag im Moment etwas komplex erscheinen, aber sie wird im weiteren Verlauf durchaus Sinn machen. Der Persistenzkontext ist der Cache der ersten Ebene, in dem alle Entitäten aus der Datenbank abgerufen oder in der Datenbank gespeichert werden . Es befindet sich zwischen unserer Anwendung und dem dauerhaften Speicher.

Der Persistenzkontext verfolgt alle Änderungen, die an einer verwalteten Entität vorgenommen wurden. Wenn sich während einer Transaktion etwas ändert, wird die Entität als fehlerhaft markiert. Nach Abschluss der Transaktion werden diese Änderungen in den dauerhaften Speicher verschoben.

Der EntityManager ist die Schnittstelle, über die wir mit dem Persistenzkontext interagieren können. Wann immer wir den EntityManager verwenden , interagieren wir tatsächlich mit dem Persistenzkontext .

Wenn bei jeder in der Entität vorgenommenen Änderung der permanente Speicher aufgerufen wird, können wir uns vorstellen, wie viele Anrufe getätigt werden. Dies führt zu Leistungseinbußen, da dauerhafte Speicheraufrufe teuer sind.

3. Persistenzkontexttyp

Es gibt zwei Arten von Persistenzkontexten:

  • Persistenzkontext mit Transaktionsbereich
  • Persistenzkontext mit erweitertem Gültigkeitsbereich

Werfen wir einen Blick auf jeden.

3.1 Transaktionsbezogener Persistenzkontext

Der Transaktionspersistenzkontext ist an die Transaktion gebunden. Sobald die Transaktion abgeschlossen ist, werden die im Persistenzkontext vorhandenen Entitäten in den persistenten Speicher verschoben.

Wenn wir eine Operation innerhalb der Transaktion ausführen, sucht der EntityManager nach einem Persistenzkontext . Wenn einer existiert, wird er verwendet. Andernfalls wird ein Persistenzkontext erstellt.

Der Standardkontexttyp für die Persistenz ist PersistenceContextType.TRANSACTION . Um den EntityManager anzuweisen, den Transaktionspersistenzkontext zu verwenden, kommentieren wir ihn einfach mit @PersistenceContext :

@PersistenceContext private EntityManager entityManager;

3.2 Persistenzkontext mit erweitertem Gültigkeitsbereich

Ein erweiterter Persistenzkontext kann sich über mehrere Transaktionen erstrecken. Wir können die Entität ohne die Transaktion beibehalten, sie jedoch nicht ohne eine Transaktion leeren.

Um EntityManager anzuweisen, einen Persistenzkontext mit erweitertem Gültigkeitsbereich zu verwenden, müssen wir das type- Attribut von @PersistenceContext anwenden :

@PersistenceContext(type = PersistenceContextType.EXTENDED) private EntityManager entityManager;

In der zustandslosen Sitzungs-Bean kennt der erweiterte Persistenzkontext in einer Komponente keinen Persistenzkontext einer anderen Komponente . Dies gilt auch dann, wenn sich beide in derselben Transaktion befinden.

Angenommen, wir behalten eine Entität in einer Methode von Komponente A bei , die in einer Transaktion ausgeführt wird. Wir nennen dann eine Methode der Komponente B . Im Methodenpersistenzkontext von Komponente B finden wir die Entität, die wir zuvor in der Methode von Komponente A beibehalten haben, nicht .

4. Beispiel für einen Persistenzkontext

Jetzt, da wir genug über den Persistenzkontext wissen, ist es Zeit, in ein Beispiel einzutauchen. Wir werden verschiedene Anwendungsfälle mit Transaktionspersistenzkontext und erweitertem Persistenzkontext durchführen.

Zuerst erstellen wir unsere Serviceklasse TransctionPersistenceContextUserService :

@Component public class TransctionPersistenceContextUserService { @PersistenceContext private EntityManager entityManager; @Transactional public User insertWithTransaction(User user) { entityManager.persist(user); return user; } public User insertWithoutTransaction(User user) { entityManager.persist(user); return user; } public User find(long id) { return entityManager.find(User.class, id); } }

Die nächste Klasse, ExtendedPersistenceContextUserService , ist der obigen sehr ähnlich, mit Ausnahme der Annotation @PersistenceContext . Diesmal geben wir PersistenceContextType.EXTENDED in den Typ - Parameter seiner @PersistenceContext Anmerkung:

@Component public class ExtendedPersistenceContextUserService { @PersistenceContext(type = PersistenceContextType.EXTENDED) private EntityManager entityManager; // Remaining code same as above }

5. Testfälle

Nachdem wir unsere Serviceklassen eingerichtet haben, ist es an der Zeit, verschiedene Anwendungsfälle mit Transaktionspersistenzkontext und erweitertem Persistenzkontext zu erstellen.

5.1 Testen des Transaktionspersistenzkontexts

Lassen Sie uns eine Benutzerentität im transaktionsbezogenen Persistenzkontext beibehalten. Die Entität wird in einem dauerhaften Speicher gespeichert. Anschließend überprüfen wir dies, indem wir mit dem EntityManager unseres erweiterten Persistenzkontexts einen Suchaufruf tätigen :

User user = new User(121L, "Devender", "admin"); transctionPersistenceContext.insertWithTransaction(user); User userFromTransctionPersistenceContext = transctionPersistenceContext .find(user.getId()); assertNotNull(userFromTransctionPersistenceContext); User userFromExtendedPersistenceContext = extendedPersistenceContext .find(user.getId()); assertNotNull(userFromExtendedPersistenceContext);

When we try to insert a User entity without a transaction, then TransactionRequiredException will be thrown:

@Test(expected = TransactionRequiredException.class) public void testThatUserSaveWithoutTransactionThrowException() { User user = new User(122L, "Devender", "admin"); transctionPersistenceContext.insertWithoutTransaction(user); }

5.2 Testing Extended Persistence Context

Next, let's persist the user with an extended persistence context and without a transaction. The User entity will be saved in the persistence context (cache) but not in persistent storage:

User user = new User(123L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction(user); User userFromExtendedPersistenceContext = extendedPersistenceContext .find(user.getId()); assertNotNull(userFromExtendedPersistenceContext); User userFromTransctionPersistenceContext = transctionPersistenceContext .find(user.getId()); assertNull(userFromTransctionPersistenceContext);

In the persistence context for any persistent entity identity, there will be a unique entity instance. If we try to persist another entity with the same identifier:

@Test(expected = EntityExistsException.class) public void testThatPersistUserWithSameIdentifierThrowException() { User user1 = new User(126L, "Devender", "admin"); User user2 = new User(126L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction(user1); extendedPersistenceContext.insertWithoutTransaction(user2); }

We'll see EntityExistsException:

javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session

Extended persistence context within a transaction saves the entity in persistent storage at the end of the transaction:

User user = new User(127L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction(user); User userFromDB = transctionPersistenceContext.find(user.getId()); assertNotNull(userFromDB);

Der erweiterte Persistenzkontext löscht die zwischengespeicherten Entitäten in den persistenten Speicher, wenn sie innerhalb der Transaktion verwendet werden . Erstens behalten wir die Entität ohne Transaktion bei. Als Nächstes behalten wir eine andere Entität in der Transaktion bei:

User user1 = new User(124L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction(user1); User user2 = new User(125L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction(user2); User user1FromTransctionPersistenceContext = transctionPersistenceContext .find(user1.getId()); assertNotNull(user1FromTransctionPersistenceContext); User user2FromTransctionPersistenceContext = transctionPersistenceContext .find(user2.getId()); assertNotNull(user2FromTransctionPersistenceContext);

6. Fazit

In diesem Tutorial haben wir ein gutes Verständnis für den Persistenzkontext gewonnen.

Zunächst haben wir uns den Transaktionspersistenzkontext angesehen, der während der gesamten Laufzeit der Transaktion besteht. Anschließend haben wir uns den erweiterten Persistenzkontext angesehen, der sich über mehrere Transaktionen erstrecken kann.

Wie immer ist der Beispielcode über GitHub verfügbar.