JPA / Hibernate Persistence Context

1. Oversigt

Persistensudbydere som Hibernate bruger persistens-kontekst til at styre enhedens livscyklus i en applikation.

I denne vejledning starter vi med introduktionen af ​​persistens-konteksten, så ser vi, hvorfor det er vigtigt. Endelig ser vi forskellen mellem transaktion-scoped persistens-kontekst og extended-scoped-persistence-kontekst med eksempler.

2. Persistens Kontekst

Lad os se på den officielle definition af Persistence Context:

En EntityManager-forekomst er forbundet med en persistens-kontekst. En persistens-kontekst er et sæt enhedsforekomster, hvor der for enhver vedvarende enhedsidentitet er en unik entitetsforekomst. Inden for persistens-konteksten styres enhedens forekomster og deres livscyklus. EntityManager API bruges til at oprette og fjerne vedvarende enhedsforekomster, for at finde enheder efter deres primære nøgle og forespørge efter enheder.

Ovenstående erklæring kan virke lidt kompleks lige nu, men det giver fuld mening, når vi fortsætter. Persistens-konteksten er cache på første niveau, hvor alle enheder hentes fra databasen eller gemmes i databasen. Det sidder mellem vores applikation og vedvarende opbevaring.

Persistens-kontekst holder styr på alle ændringer, der er foretaget i en administreret enhed. Hvis der ændres noget under en transaktion, markeres enheden som beskidt. Når transaktionen er gennemført, skylles disse ændringer i vedvarende opbevaring.

Det EntityManager er grænsefladen, der lader os interagere med persistens-konteksten. Når vi bruger EntityManager, vi interagerer faktisk med vedholdenhedskonteksten.

Hvis hver ændring foretaget i enheden foretager et opkald til vedvarende lagring, kan vi forestille os, hvor mange opkald der foretages. Dette vil medføre en præstationspåvirkning, fordi vedvarende lageropkald er dyre.

3. Persistens Kontekst Type

Persistens-sammenhænge findes i to typer:

  • Transaktion-scoped persistens kontekst
  • Kontekst med langvarig vedholdenhed

Lad os se på hver.

3.1 Transition-Scoped Persistence Context

Transaktionens vedvarende kontekst er bundet til transaktionen. Så snart transaktionen er afsluttet, skylles de enheder, der er til stede i persistens-sammenhængen, til vedvarende lagring.

Når vi udfører enhver handling inde i transaktionen, EntityManager kontrollerer for en persistens-kontekst. Hvis der findes en, vil den blive brugt. Ellers vil det skabe en vedholdenhedskontekst.

Standard persistens-konteksttype erPersistenceContextType.TRANSACTION. At fortælle det EntityManager for at bruge transaktionens vedvarende kontekst, kommenterer vi det simpelthen med @PersistenceContext:

@PersistenceContext private EntityManager entityManager;

3.2 Kontekst med udvidet perspektiv

En udvidet persistens-kontekst kan spænde over flere transaktioner. Vi kan opretholde enheden uden transaktionen, men kan ikke skylle den uden en transaktion.

At fortælle EntityManager for at bruge udvidet omfangsholdbarhedskontekst skal vi anvende type attribut for @PersistenceContext:

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

I den statsløse session bønne, den udvidet persistens-kontekst i en komponent er fuldstændig uvidende om nogen persistens-kontekst for en anden komponent. Dette gælder, selvom begge er i samme transaktion.

Lad os sige, at vi vedvarer en eller anden enhed i en metode til komponent EN, der kører i en transaktion. Vi kalder derefter en eller anden metode til komponent B. I komponent B'er metode persistens kontekst, vil vi ikke finde den enhed, vi har fastholdt tidligere i metoden til komponent EN.

4. Eksempel på udholdenhedskontekst

Nu hvor vi ved nok om persistens-sammenhæng, er det tid til at dykke ned i et eksempel. Vi laver forskellige brugssager med transaktionspersistenskontekst og udvidet persistenskontekst.

Lad os først oprette vores serviceklasse, TransctionPersistenceContextUserService:

@Komponent offentlig klasse TransctionPersistenceContextUserService {@PersistenceContext private EntityManager entityManager; @Transactional public User insertWithTransaction (User user) {entityManager.persist (user); tilbagevendende bruger } offentlig bruger insertWithoutTransaction (brugerbruger) {entityManager.persist (bruger); tilbagevendende bruger } offentlig brugersøgning (lang id) {return entityManager.find (User.class, id); }}

Den næste klasse, ExtendedPersistenceContextUserService, ligner meget ovenstående bortset fra @PersistenceContext kommentar. Denne gang passerer vi PersistenceContextType.EXTENDED ind i type parameter for dens @PersistenceContext kommentar:

@Komponent offentlig klasse ExtendedPersistenceContextUserService {@PersistenceContext (type = PersistenceContextType.EXTENDED) privat EntityManager entityManager; // Resterende kode samme som ovenfor}

5. Test tilfælde

Nu hvor vi har oprettet vores serviceklasser, er det tid til at gøre forskellige brugssager med transaktionspersistenskontekst og udvidet persistenskontekst.

5.1 Test af transaktionspersistens-kontekst

Lad os fortsætte a Bruger enhed, der bruger transaktionsscoped-persistens-kontekst. Enheden gemmes i vedvarende lagring. Vi bekræfter derefter ved at foretage et find-opkald ved hjælp af vores udvidede persistens-kontekster EntityManager:

Brugerbruger = ny bruger (121L, "Devender", "admin"); transctionPersistenceContext.insertWithTransaction (bruger); Bruger userFromTransctionPersistenceContext = transctionPersistenceContext .find (user.getId ()); assertNotNull (userFromTransctionPersistenceContext); Bruger userFromExtendedPersistenceContext = extendedPersistenceContext .find (user.getId ()); assertNotNull (userFromExtendedPersistenceContext);

Når vi prøver at indsætte en Bruger enhed uden en transaktion, så TransactionRequiredException vil blive kastet:

@Test (forventet = TransactionRequiredException.class) offentlig ugyldig testThatUserSaveWithoutTransactionThrowException () {Brugerbruger = ny bruger (122L, "Devender", "admin"); transctionPersistenceContext.insertWithoutTransaction (bruger); }

5.2 Test af udvidet persistens-kontekst

Lad os derefter fortsætte brugeren med en udvidet persistens-kontekst og uden en transaktion. Det Bruger enhed vil blive gemt i persistens-kontekst (cache), men ikke i vedvarende lager:

Brugerbruger = ny bruger (123L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction (bruger); Bruger userFromExtendedPersistenceContext = extendedPersistenceContext .find (user.getId ()); assertNotNull (userFromExtendedPersistenceContext); Bruger userFromTransctionPersistenceContext = transctionPersistenceContext .find (user.getId ()); assertNull (userFromTransctionPersistenceContext);

I vedholdenhedskonteksten for enhver vedvarende enhedsidentitet vil der være en unik entitetsforekomst. Hvis vi forsøger at fastholde en anden enhed med samme identifikator:

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

Vi får at se EntityExistsException:

javax.persistence.EntityExistsException: Et andet objekt med samme identifikationsværdi var allerede knyttet til sessionen

Udvidet persistens-kontekst inden for en transaktion gemmer enheden i vedvarende lagring i slutningen af ​​transaktionen:

Brugerbruger = ny bruger (127L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction (bruger); Bruger userFromDB = transctionPersistenceContext.find (user.getId ()); assertNotNull (userFromDB);

Den udvidede persistens-kontekst skyller de cachelagrede enheder i vedvarende lagring, når de bruges i transaktionen. For det første vedvarer vi enheden uden en transaktion. Dernæst fortsætter vi en anden enhed i transaktionen:

Brugerbruger1 = ny bruger (124L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction (user1); Brugerbruger2 = ny bruger (125L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction (user2); Brugerbruger1FromTransctionPersistenceContext = transctionPersistenceContext .find (user1.getId ()); assertNotNull (user1FromTransctionPersistenceContext); Bruger user2FromTransctionPersistenceContext = transctionPersistenceContext .find (user2.getId ()); assertNotNull (user2FromTransctionPersistenceContext);

6. Konklusion

I denne vejledning fik vi en god forståelse af persistens-konteksten.

For det første så vi på transaktionens vedvarende kontekst, der eksisterer gennem hele transaktionens løbetid. Derefter så vi på den udvidede persistens-kontekst, som kan spænde over flere transaktioner.

Som altid er prøvekoden tilgængelig på GitHub.