Context van JPA / Hibernate Persistence

1. Overzicht

Persistentieproviders zoals Hibernate maken gebruik van persistentiecontext om de levenscyclus van de entiteit in een applicatie te beheren.

In deze tutorial beginnen we met de introductie van de persistentie-context, daarna zullen we zien waarom het belangrijk is. Ten slotte zullen we het verschil zien tussen persistentiecontext met een transactiebereik en persistentiecontext met een uitgebreid bereik met voorbeelden.

2. Persistentiecontext

Laten we eens kijken naar de officiële definitie van de persistentiecontext:

Een EntityManager-instantie is gekoppeld aan een persistentiecontext. Een persistentiecontext is een set entiteitsinstances waarin voor elke persistente entiteitsidentiteit een unieke entiteitsinstantie is. Binnen de persistentiecontext worden de entiteitsinstances en hun levenscyclus beheerd. De EntityManager API wordt gebruikt om persistente entiteitsinstances te maken en te verwijderen, entiteiten te vinden op basis van hun primaire sleutel, en om over entiteiten te vragen.

De bovenstaande verklaring lijkt op dit moment misschien een beetje ingewikkeld, maar het zal volkomen logisch zijn naarmate we verder gaan. De persistentiecontext is de cache op het eerste niveau waar alle entiteiten uit de database worden opgehaald of in de database worden opgeslagen. Het bevindt zich tussen onze applicatie en permanente opslag.

Persistentiecontext houdt alle wijzigingen bij die in een beheerde entiteit zijn aangebracht. Als er iets verandert tijdens een transactie, wordt de entiteit gemarkeerd als vuil. Wanneer de transactie is voltooid, worden deze wijzigingen doorgespoeld naar permanente opslag.

De EntityManager is de interface waarmee we kunnen communiceren met de persistentiecontext. Elke keer dat we de EntityManager, we zijn eigenlijk in wisselwerking met de persistentiecontext.

Als elke wijziging die in de entiteit wordt aangebracht, een oproep doet naar permanente opslag, kunnen we ons voorstellen hoeveel oproepen er zullen worden gedaan. Dit heeft een invloed op de prestaties omdat aanhoudende opslagoproepen duur zijn.

3. Contexttype persistentie

Persistentiecontexten zijn beschikbaar in twee typen:

  • Persistentiecontext op basis van transactiebereik
  • Persistentiecontext met een uitgebreid bereik

Laten we ze allemaal eens bekijken.

3.1 Persistentiecontext op basis van transactie

De transactiepersistentiecontext is gebonden aan de transactie. Zodra de transactie is voltooid, worden de entiteiten die aanwezig zijn in de persistentiecontext naar permanente opslag gespoeld.

Wanneer we een bewerking uitvoeren binnen de transactie, wordt de EntityManager controleert op een persistentiecontext. Als er een bestaat, wordt deze gebruikt. Anders zal het een persistentiecontext creëren.

Het standaardcontexttype persistentie isPersistenceContextType.TRANSACTION. Om het te vertellen EntityManager om de transactiepersistentiecontext te gebruiken, annoteren we deze gewoon met @PersistenceContext:

@PersistenceContext private EntityManager entityManager;

3.2 Persistentiecontext met een uitgebreid bereik

Een uitgebreide persistentiecontext kan zich over meerdere transacties uitstrekken. We kunnen de entiteit zonder de transactie voortzetten, maar kunnen deze niet doorspoelen zonder een transactie.

Vertellen EntityManager om persistentiecontext met een uitgebreid bereik te gebruiken, moeten we de type kenmerk van @PersistenceContext:

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

In de staatloze sessieboon, de verlengde persistentiecontext in één component is zich totaal niet bewust van enige persistentiecontext van een andere component. Dit geldt zelfs als beide in dezelfde transactie zitten.

Laten we zeggen dat we een entiteit behouden in een methode van Component EEN, die wordt uitgevoerd in een transactie. We noemen dan een methode van Component B. In Component B's methode persistentie context, zullen we de entiteit die we eerder hebben volgehouden niet vinden in de methode van Component EEN.

4. Persistentiecontextvoorbeeld

Nu we genoeg weten over persistentiecontext, is het tijd om in een voorbeeld te duiken. We zullen verschillende use-cases maken met de context van transactiepersistentie en uitgebreide persistentiecontext.

Laten we eerst onze serviceklasse creëren, TransctionPersistenceContextUserService:

@Component openbare klasse TransctionPersistenceContextUserService {@PersistenceContext private EntityManager entityManager; @Transactional openbare gebruiker insertWithTransaction (gebruiker gebruiker) {entityManager.persist (gebruiker); terugkeer gebruiker; } openbare gebruiker insertWithoutTransaction (gebruiker gebruiker) {entityManager.persist (gebruiker); terugkeer gebruiker; } openbare gebruiker zoeken (lange id) {return entityManager.find (User.class, id); }}

De volgende klas, ExtendedPersistenceContextUserService, lijkt erg op het bovenstaande, behalve de @PersistenceContext annotatie. Deze keer passeren we PersistenceContextType.EXTENDED in de type parameter van zijn @PersistenceContext annotatie:

@Component openbare klasse ExtendedPersistenceContextUserService {@PersistenceContext (type = PersistenceContextType.EXTENDED) privé EntityManager entityManager; // Resterende code hetzelfde als hierboven}

5. Testgevallen

Nu we onze serviceklassen hebben ingesteld, is het tijd om verschillende gebruiksscenario's te maken met context voor transactiepersistentie en uitgebreide persistentiecontext.

5.1 Context van transactiepersistentie testen

Laten we doorgaan a Gebruiker entiteit die gebruikmaakt van persistentiecontext op basis van een transactie. De entiteit wordt opgeslagen in permanente opslag. Vervolgens verifiëren we door een zoekoproep te doen met behulp van onze uitgebreide persistentiecontext EntityManager:

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

Wanneer we proberen een Gebruiker entiteit zonder transactie TransactionRequiredException zal worden gegooid:

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

5.2 Testen van Extended Persistence Context

Laten we vervolgens de gebruiker vasthouden met een uitgebreide persistentiecontext en zonder een transactie. De Gebruiker entiteit wordt opgeslagen in de persistentiecontext (cache) maar niet in permanente opslag:

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

In de persistentiecontext voor elke persistente entiteitsidentiteit, zal er een unieke entiteitsinstantie zijn. Als we proberen een andere entiteit met dezelfde ID te behouden:

@Test (verwacht = EntityExistsException.class) public void testThatPersistUserWithSameIdentifierThrowException () {Gebruiker user1 = nieuwe gebruiker (126L, "Devender", "admin"); Gebruiker user2 = nieuwe gebruiker (126L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction (gebruiker1); extendedPersistenceContext.insertWithoutTransaction (user2); }

We zullen zien EntityExistsException:

javax.persistence.EntityExistsException: er was al een ander object met dezelfde ID-waarde aan de sessie gekoppeld

Uitgebreide persistentiecontext binnen een transactie slaat de entiteit op in permanente opslag aan het einde van de transactie:

Gebruiker gebruiker = nieuwe gebruiker (127L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction (gebruiker); Gebruiker userFromDB = transctionPersistenceContext.find (user.getId ()); assertNotNull (userFromDB);

De uitgebreide persistentie-context verwijdert de in de cache opgeslagen entiteiten in permanente opslag wanneer deze binnen de transactie wordt gebruikt. Ten eerste zetten we de entiteit voort zonder een transactie. Vervolgens zetten we een andere entiteit in de transactie voort:

Gebruiker user1 = nieuwe gebruiker (124L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction (gebruiker1); Gebruiker user2 = nieuwe gebruiker (125L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction (user2); Gebruiker user1FromTransctionPersistenceContext = transctionPersistenceContext .find (user1.getId ()); assertNotNull (user1FromTransctionPersistenceContext); Gebruiker user2FromTransctionPersistenceContext = transctionPersistenceContext .find (user2.getId ()); assertNotNull (user2FromTransctionPersistenceContext);

6. Conclusie

In deze tutorial hebben we een goed begrip gekregen van de persistentie-context.

Eerst hebben we gekeken naar de context van transactiepersistentie, die gedurende de hele looptijd van de transactie bestaat. Vervolgens hebben we gekeken naar de uitgebreide persistentiecontext, die zich over meerdere transacties kan uitstrekken.

Zoals altijd is de voorbeeldcode beschikbaar op GitHub.