Dvaletilstandslevecyklus

1. Oversigt

Hver dvale-enhed har naturligvis en livscyklus inden for rammerne - den er enten i en forbigående, administreret, løsrevet eller slettet tilstand.

At forstå disse tilstande på både begrebsmæssigt og teknisk niveau er afgørende for at kunne bruge dvale korrekt.

For at lære om forskellige dvale-metoder, der beskæftiger sig med enheder, skal du kigge på en af ​​vores tidligere tutorials.

2. Hjælpemetoder

I hele denne tutorial bruger vi konsekvent flere hjælpemetoder:

  • HibernateLifecycleUtil.getManagedEntities (session) - vi bruger det til at få alle administrerede enheder fra en Sessionens intern butik
  • DirtyDataInspector.getDirtyEntities () - vi bruger denne metode til at få en liste over alle enheder, der blev markeret som 'beskidte'
  • HibernateLifecycleUtil.queryCount (forespørgsel) - en praktisk metode at gøre tælle(*) forespørgsel mod den integrerede database

Alle ovenstående hjælpemetoder importeres statisk for bedre læsbarhed. Du kan finde deres implementeringer i GitHub-projektet, der er knyttet i slutningen af ​​denne artikel.

3. Det handler om vedholdenhedskontekst

Før vi går ind i emnet for enhedens livscyklus, skal vi først forstå vedholdenhedskonteksten.

Kort sagt, den vedholdenhedskontekst sidder mellem klientkode og datalager. Det er et iscenesættelsesområde, hvor vedvarende data konverteres til enheder, klar til at blive læst og ændret af klientkode.

Teoretisk set er vedholdenhedskontekst er en implementering af enhedens arbejdsmønster. Det holder styr på alle indlæste data, sporer ændringer af disse data og er ansvarlig for til sidst at synkronisere eventuelle ændringer tilbage til databasen i slutningen af ​​forretningstransaktionen.

JPA EntityManager og dvale Session er en implementering af vedholdenhedskontekst koncept. I hele denne artikel bruger vi dvale Session at repræsentere vedholdenhedskontekst.

Dvaletilstandenes livscyklustilstand forklarer, hvordan enheden er relateret til en vedholdenhedskontekst, som vi ser næste.

4. Administreret enhed

En administreret enhed er en repræsentation af en databasetabelrække (selvom den række endnu ikke behøver at eksistere i databasen).

Dette styres af den aktuelt kørende Sessionog hver ændring, der foretages på den, spores og udbredes automatisk til databasen.

Det Session enten indlæser enheden fra databasen eller vedhæfter en løsrevet enhed igen. Vi diskuterer løsrevne enheder i afsnit 5.

Lad os overholde nogle koder for at få afklaring.

Vores prøveapplikation definerer en enhed, den Fodboldspiller klasse. Ved opstart initialiserer vi datalageret med nogle eksempler på data:

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

Lad os sige, at vi vil ændre navnet på Buffon til at begynde med - vi vil sætte hans fulde navn i Gianluigi Buffon i stedet for Gigi Buffon.

Først skal vi starte vores arbejdsenhed ved at skaffe en Session:

Session session = sessionFactory.openSession ();

I et servermiljø indsprøjter vi muligvis en Session til vores kode via en kontekstbevidst proxy. Princippet forbliver det samme: vi har brug for en Session at indkapsle forretningstransaktionen for vores arbejdsenhed.

Derefter instruerer vi vores Session for at indlæse data fra den vedvarende butik:

assertThat (getManagedEntities (session)). er tom (); Liste spillere = s.createQuery ("fra FootballPlayer"). GetResultList (); assertThat (getManagedEntities (session)). størrelse (). isEqualTo (3); 

Når vi først får en Session, dens vedvarende kontekstbutik er tom, som det fremgår af vores første hævde udmelding.

Dernæst udfører vi en forespørgsel, der henter data fra databasen, opretter enhedsrepræsentation af dataene og endelig returnerer enheden, som vi kan bruge.

Internt er den Session holder styr på alle enheder, den indlæser i den vedvarende kontekstlager. I vores tilfælde er Sessionens intern butik vil indeholde 3 enheder efter forespørgslen.

Lad os nu ændre Gigis navn:

Transaktionstransaktion = session.getTransaction (); transaction.begin (); FootballPlayer gigiBuffon = players.stream () .filter (p -> p.getId () == 3) .findFirst () .get (); gigiBuffon.setName ("Gianluigi Buffon"); transaktion.forpligtelse (); assertThat (getDirtyEntities ()). størrelse (). isEqualTo (1); assertThat (getDirtyEntities (). get (0) .getName ()). isEqualTo ("Gianluigi Buffon");

4.1. Hvordan virker det?

Opkald til transaktion begå() eller Flush(), det Session vil finde nogen snavset enheder fra dens sporingsliste og synkroniserer tilstanden til databasen.

Bemærk, at vi ikke behøvede at kalde nogen metode til at underrette Session at vi har ændret noget i vores enhed - da det er en administreret enhed, overføres alle ændringer automatisk til databasen.

En administreret enhed er altid en vedvarende enhed - den skal have en database-id, selvom databaserækkerepræsentationen endnu ikke er oprettet, dvs. INSERT-sætningen afventer slutningen på arbejdsenheden.

Se kapitlet om forbigående enheder nedenfor.

5. Fritliggende enhed

EN løsrevet enhed er bare en almindelig enhed POJO hvis identitetsværdi svarer til en databaserække. Forskellen fra en administreret enhed er, at den er ikke spores længere af nogen vedholdenhedskontekst.

En enhed kan løsrive sig, når Session bruges til at indlæse det var lukket, eller når vi ringer Session.evict (enhed) eller Session.clear ().

Lad os se det i koden:

FootballPlayer cr7 = session.get (FootballPlayer.class, 1L); assertThat (getManagedEntities (session)). størrelse (). isEqualTo (1); assertThat (getManagedEntities (session) .get (0) .getId ()). isEqualTo (cr7.getId ()); session.evict (cr7); assertThat (getManagedEntities (session)). størrelse (). isEqualTo (0);

Vores persistens-kontekst sporer ikke ændringerne i løsrevne enheder:

cr7.setName ("CR7"); transaktion.forpligtelse (); assertThat (getDirtyEntities ()). er tom ();

Session.merge (enhed) / Session.update (enhed) kan (gen) vedhæft en session:

FootballPlayer messi = session.get (FootballPlayer.class, 2L); session.evict (messi); messi.setName ("Leo Messi"); transaktion.forpligtelse (); assertThat (getDirtyEntities ()). er tom (); transaktion = startTransaktion (session); session.update (messi); transaktion.forpligtelse (); assertThat (getDirtyEntities ()). størrelse (). er EqualTo (1); assertThat (getDirtyEntities (). get (0) .getName ()). isEqualTo ("Leo Messi");

Til reference på begge Session.merge () og Session.update () se her.

5.1. Identitetsfeltet er alt, hvad der betyder noget

Lad os se på følgende logik:

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

I eksemplet ovenfor har vi iværksat en enhed på den sædvanlige måde via dens konstruktør. Vi har udfyldt felterne med værdier, og vi har indstillet identiteten til 3, hvilket svarer til identiteten af ​​vedvarende data, der tilhører Gigi Buffon. Ringer opdater () har nøjagtig den samme effekt, som hvis vi har indlæst enheden fra en anden vedholdenhedskontekst.

Faktisk, Session skelner ikke, hvor en genbundet enhed stammer fra.

Det er et ganske almindeligt scenario i webapplikationer at konstruere løsrevne enheder fra HTML-formværdier.

Så vidt som Session vedrører, er en løsrevet enhed bare en almindelig enhed, hvis identitetsværdi svarer til vedvarende data.

Vær opmærksom på, at eksemplet ovenfor kun tjener et demo-formål. og vi skal vide nøjagtigt, hvad vi laver. Ellers kan vi ende med nulværdier på tværs af vores enhed, hvis vi bare indstiller værdien på det felt, vi vil opdatere, så resten forbliver uberørt (så faktisk nul).

6. Forbigående enhed

En kortvarig enhed er simpelthen en enhed objekt, der ikke er repræsenteret i den vedvarende butik og administreres ikke af nogen Session.

Et typisk eksempel på en kortvarig enhed ville være at instantere en ny enhed via dens konstruktør.

At skabe en kortvarig enhed vedholdende, vi skal ringe Session.save (enhed) eller Session.saveOrUpdate (enhed):

FootballPlayer neymar = ny FootballPlayer (); neymar.setName ("Neymar"); session.save (neymar); assertThat (getManagedEntities (session)). størrelse (). isEqualTo (1); assertThat (neymar.getId ()). erNotNull (); int count = queryCount ("vælg count (*) fra Football_Player hvor name =" Neymar ""); assertThat (count) .isEqualTo (0); transaktion.forpligtelse (); count = queryCount ("vælg count (*) fra Football_Player hvor name =" Neymar ""); assertThat (count) .isEqualTo (1);

Så snart vi udfører Session.save (enhed), tildeles enheden en identitetsværdi og administreres af Session. Det er dog muligvis endnu ikke tilgængeligt i databasen, da INSERT-operationen muligvis bliver forsinket til slutningen af ​​arbejdsenheden.

7. Slettet enhed

En enhed er i en slettet (fjernet) tilstand, hvis Session.delete (enhed) er blevet kaldt, og Session har markeret enheden til sletning. Selve kommandoen SLET udstedes muligvis i slutningen af ​​arbejdsenheden.

Lad os se det i følgende kode:

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

Bemærk dog, at enheden forbliver i den vedvarende kontekstlager indtil slutningen af ​​arbejdsenheden.

8. Konklusion

Begrebet vedholdenhedskontekst er central for at forstå livscyklussen for dvaleenheder. Vi har afklaret livscyklussen ved at se på kodeeksemplerne, der viser hver status.

Som sædvanlig kan koden, der bruges i denne artikel, findes på GitHub.