Životni ciklus hibernacije entiteta

1. Pregled

Svaki hibernacijski entitet prirodno ima životni ciklus unutar okvira - on je ili u prijelaznom, upravljanom, odvojenom ili izbrisanom stanju.

Razumijevanje ovih stanja na konceptualnoj i tehničkoj razini neophodno je za pravilno korištenje hibernacije.

Da biste saznali više o raznim metodama hibernacije koje se bave entitetima, pogledajte jedan od naših prethodnih vodiča.

2. Metode pomoćnika

Tijekom ovog vodiča dosljedno ćemo koristiti nekoliko pomoćnih metoda:

  • HibernateLifecycleUtil.getManagedEntities (sesija) - koristit ćemo ga za dobivanje svih upravljanih entiteta iz a Sjednice interna trgovina
  • DirtyDataInspector.getDirtyEntities () - koristit ćemo ovu metodu kako bismo dobili popis svih entiteta koji su označeni kao 'prljavi'
  • HibernateLifecycleUtil.queryCount (upit) - prikladna metoda računati(*) upit prema ugrađenoj bazi podataka

Sve gore navedene pomoćne metode su statički uvezene radi bolje čitljivosti. Njihove implementacije možete pronaći u projektu GitHub povezanom na kraju ovog članka.

3. Sve je u kontekstu postojanosti

Prije ulaska u temu životnog ciklusa entiteta, prvo moramo razumjeti kontekst postojanosti.

Jednostavno rečeno, kontekst postojanosti nalazi se između klijentskog koda i spremišta podataka. To je scensko područje u kojem se trajni podaci pretvaraju u entitete, spremne za čitanje i izmjenu klijentskim kodom.

Teoretski gledano, kontekst postojanosti je provedba obrasca Jedinice rada. Prati sve učitane podatke, prati promjene tih podataka i odgovoran je da na kraju sinkronizira sve promjene natrag u bazu podataka na kraju poslovne transakcije.

JPA EntityManager i Hibernate's Sjednica su provedba kontekst postojanosti koncept. Kroz ovaj ćemo članak koristiti Hibernate Sjednica predstavljati kontekst postojanosti.

Stanje životnog ciklusa entiteta hibernacije objašnjava kako je entitet povezan sa kontekst postojanosti, kao što ćemo vidjeti dalje.

4. Upravljani entitet

Upravljani entitet predstavlja red retka tablice baze podataka (iako taj redak još ne mora postojati u bazi podataka).

To upravlja trenutno pokrenutom Sjednica, i svaka promjena na njemu automatski će se pratiti i proširiti u bazu podataka.

The Sjednica ili učitava entitet iz baze podataka ili ponovno pridružuje odvojeni entitet. O odvojenim entitetima razgovarat ćemo u odjeljku 5.

Promatrajmo neki kod da bismo dobili pojašnjenje.

Naš uzorak aplikacije definira jedan entitet, Nogometaš razred. Prilikom pokretanja pohranit ćemo podatke u neke uzorke podataka:

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

Recimo da za početak želimo promijeniti ime Buffon - želimo staviti njegovo puno ime Gianluigi Buffon umjesto Gigi Buffon.

Prvo, svoju jedinicu rada moramo započeti dobivanjem a Sjednica:

Sjednica sesije = sessionFactory.openSession ();

U poslužiteljsko okruženje možemo ubrizgati a Sjednica na naš kod putem proxyja koji zna kontekst. Princip ostaje isti: trebamo Sjednica za inkapsuliranje poslovne transakcije naše jedinice rada.

Dalje ćemo uputiti naše Sjednica za učitavanje podataka iz trajnog spremišta:

assertThat (getManagedEntities (sesija)). isEmpty (); Popis igrača = s.createQuery ("iz FootballPlayer"). GetResultList (); assertThat (getManagedEntities (sesija)). size (). isEqualTo (3); 

Kada prvi put dobijemo a Sjednica, njegova trajna pohrana konteksta je prazna, kao što pokazuje naša prva tvrditi izjava.

Dalje, izvršavamo upit koji dohvaća podatke iz baze podataka, stvara entitet koji predstavlja podatke i na kraju vraća entitet koji ćemo koristiti.

Interno, Sjednica prati sve entitete koje učita u trajnoj spremištu konteksta. U našem slučaju, Sjednice interna pohrana sadržavat će 3 entiteta nakon upita.

Promijenimo sada Gigijevo ime:

Transakcija transakcija = session.getTransaction (); transaction.begin (); FootballPlayer gigiBuffon = igrači.stream () .filter (p -> p.getId () == 3) .findFirst () .get (); gigiBuffon.setName ("Gianluigi Buffon"); transakcija.commit (); assertThat (getDirtyEntities ()). size (). isEqualTo (1); assertThat (getDirtyEntities (). get (0) .getName ()). isEqualTo ("Gianluigi Buffon");

4.1. Kako radi?

Na poziv za transakciju počiniti() ili isprati (), Sjednica naći će bilo koji prljav entiteta s popisa za praćenje i sinkroniziraju stanje s bazom podataka.

Primijetite da nismo trebali nazvati nijedan način za obavještavanje Sjednica da smo nešto promijenili u našem entitetu - budući da se radi o upravljanom entitetu, sve se promjene automatski prenose u bazu podataka.

Upravljani entitet uvijek je trajni entitet - on mora imati identifikator baze podataka, iako reprezentacija retka baze podataka još nije kreirana, tj. INSERT izraz čeka na kraj jedinice rada.

Pogledajte dolje poglavlje o privremenim entitetima.

5. Odvojeni entitet

A odvojeni entitet je samo obični entitet POJO čija vrijednost identiteta odgovara retku baze podataka. Razlika od upravljanog entiteta je u tome što je ne prati više nitko kontekst postojanosti.

Entitet se može odvojiti kada Sjednica koristi se za utovar bilo je zatvoreno ili kad nazovemo Sesija.evict (entitet) ili Session.clear ().

Pogledajmo u kodu:

FootballPlayer cr7 = session.get (FootballPlayer.class, 1L); assertThat (getManagedEntities (sesija)). size (). isEqualTo (1); assertThat (getManagedEntities (sesija) .get (0) .getId ()). isEqualTo (cr7.getId ()); sjednica.evict (cr7); assertThat (getManagedEntities (sesija)). size (). isEqualTo (0);

Naš kontekst postojanosti neće pratiti promjene u izdvojenim entitetima:

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

Session.merge (entitet) /Session.update (entitet) može (ponovno) priložiti sesiju:

FootballPlayer messi = session.get (FootballPlayer.class, 2L); sjednica.evict (messi); messi.setName ("Leo Messi"); transakcija.commit (); assertThat (getDirtyEntities ()). isEmpty (); transakcija = startTransaction (sesija); session.update (messi); transakcija.commit (); assertThat (getDirtyEntities ()). size (). isEqualTo (1); assertThat (getDirtyEntities (). get (0) .getName ()). isEqualTo ("Leo Messi");

Za referencu na oba Session.merge () i Session.update () vidi ovdje.

5.1. Važno je samo polje identiteta

Pogledajmo sljedeću logiku:

FootballPlayer gigi = novi FootballPlayer (); gigi.setId (3); gigi.setName ("Gigi legenda"); session.update (gigi);

U gornjem primjeru instancirali smo entitet na uobičajeni način putem njegovog konstruktora. Napunili smo polja vrijednostima i postavili smo identitet na 3, što odgovara identitetu trajnih podataka koji pripadaju Gigi Buffon. Pozivanje ažuriranje() ima potpuno isti učinak kao da smo entitet učitali iz drugog kontekst postojanosti.

Zapravo, Sjednica ne razlikuje odakle ponovno pripojeni entitet potječe.

Sasvim je uobičajen scenarij u web aplikacijama za izradu odvojenih entiteta od vrijednosti HTML obrasca.

Što se tiče Sjednica što se tiče, izdvojeni entitet je samo obični entitet čija vrijednost identiteta odgovara trajnim podacima.

Imajte na umu da gornji primjer služi samo u demo svrhe. i moramo točno znati što radimo. U suprotnom, mogli bismo završiti s null vrijednostima u cijelom našem entitetu ako samo postavimo vrijednost na polje koje želimo ažurirati, a ostatak ostavimo netaknutim (dakle, efektivno null).

6. Privremeni entitet

Privremeni entitet je jednostavno entitet objekt koji nema predstavu u trajnom spremištu a njime ne upravlja nitko Sjednica.

Tipičan primjer prijelaznog entiteta bio bi instanciranje novog entiteta putem njegovog konstruktora.

Da bi se napravio prijelazni entitet uporan, moramo nazvati Sesija.save (entitet) ili Session.saveOrUpdate (entitet):

FootballPlayer neymar = novi FootballPlayer (); neymar.setName ("Neymar"); session.save (neymar); assertThat (getManagedEntities (sesija)). size (). isEqualTo (1); assertThat (neymar.getId ()). isNotNull (); int count = queryCount ("select count (*) from Football_Player where name =" Neymar ""); assertThat (count) .isEqualTo (0); transakcija.commit (); count = queryCount ("select count (*) from Football_Player where name =" Neymar ""); assertThat (count) .isEqualTo (1);

Čim izvršimo Sesija.save (entitet), entitetu se dodjeljuje vrijednost identiteta i njime upravlja Sjednica. Međutim, možda još neće biti dostupan u bazi podataka, jer se INSERT operacija može odgoditi do kraja jedinice rada.

7. Izbrisani entitet

Entitet je u izbrisanom (uklonjenom) stanju ako Session.delete (entitet) je pozvan i Sjednica je označio entitet za brisanje. Sama naredba DELETE može se izdati na kraju jedinice rada.

Pogledajmo u sljedećem kodu:

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

Međutim, primijetite da entitet ostaje u trajnoj spremištu konteksta do kraja jedinice rada.

8. Zaključak

Koncept kontekst postojanosti je presudan za razumijevanje životnog ciklusa hibernacijskih entiteta. Razjasnili smo životni ciklus proučavanjem primjera koda koji pokazuju svaki status.

Kao i obično, kod korišten u ovom članku možete pronaći na GitHubu.