Hibernate nije mogao inicijalizirati proxy - nema sesije

1. Pregled

U radu s hibernacijom mogli smo naići na pogrešku koja kaže: org.hibernate.LazyInitializationException: nije moguće inicijalizirati proxy - nema sesije.

U ovom ćemo brzom vodiču pobliže proučiti osnovni uzrok pogreške i naučiti kako je izbjeći.

2 Razumijevanje pogreške

Pristup lijeno učitanom objektu izvan konteksta otvorene sesije hibernacije rezultirat će ovom iznimkom.

Važno je razumjeti što je Sesija, Lijena inicijalizacija,i Proxy objekt i kako se okupljaju u Hibernate okvir.

  • Sjednica je kontekst postojanosti koji predstavlja razgovor između aplikacije i baze podataka
  • Lijeno učitavanje znači da se objekt neće učitati u Sjednica kontekstu dok mu se ne pristupi u kodu.
  • Hibernate stvara dinamiku Proxy objekt potklasa koja će pogoditi bazu podataka samo kad prvi put upotrijebimo objekt.

Ova pogreška znači da pokušavamo dohvatiti lijeno učitani objekt iz baze podataka pomoću proxy objekta, ali sesija hibernacije već je zatvorena.

3. Primjer za LazyInitializationException

Pogledajmo iznimku u konkretnom scenariju.

Želimo stvoriti jednostavan Korisnik objekt s pridruženim ulogama. Upotrijebimo JUnit za demonstraciju LazyInitializationException pogreška.

3.1. Hibernate Utility Class

Prvo, definirajmo a HibernateUtil razred za stvaranje a SjednicaTvornica s konfiguracijom.

Upotrijebit ćemo memoriju HSQLDB baza podataka.

3.2. Entiteti

Evo našeg Korisnik entitet:

@Entity @Table (name = "user") javni razred Korisnik {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "id") private int id; @Column (name = "first_name") private String firstName; @Column (name = "last_name") private String lastName; @OneToMany private Set uloge; } 

I pridruženi Uloga entitet:

@Entity @Table (name = "role") uloga javne klase {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "id") private int id; @Column (name = "role_name") private String roleName; }

Kao što možemo vidjeti, postoji odnos jedan-prema-mnogima Korisnik i Uloga.

3.3. Stvaranje korisnika s ulogama

Dalje, kreirajmo dvije Uloga objekti:

Administrator uloge = nova uloga ("Administrator"); Uloga dba = nova uloga ("DBA");

Zatim kreiramo Korisnik s ulogama:

Korisnik korisnik = novi korisnik ("Bob", "Smith"); user.addRole (admin); user.addRole (dba);

Napokon, možemo otvoriti sesiju i ustrajati na objektima:

Sjednica sesije = sessionFactory.openSession (); session.beginTransaction (); user.getRoles (). forEach (role -> session.save (role)); session.save (korisnik); session.getTransaction (). commit (); session.close ();

3.4. Dohvaćanje uloga

U prvom ćemo scenariju vidjeti kako na pravi način dohvatiti korisničke uloge:

@Test public void whenAccessUserRolesInsideSession_thenSuccess () {Korisnik detachedUser = createUserWithRoles (); Sjednica sesije = sessionFactory.openSession (); session.beginTransaction (); Korisnik persistentUser = session.find (User.class, detachedUser.getId ()); Assert.assertEquals (2, persistentUser.getRoles (). Size ()); session.getTransaction (). commit (); session.close (); }

Ovdje pristupamo objektu unutar sesije, stoga nema pogreške.

3.5. Neuspjeh dohvaćanja uloga

U drugom ćemo scenariju nazvati a getRoles metoda izvan sesije:

@Test public void whenAccessUserRolesOutsideSession_thenThrownException () {Korisnik detachedUser = createUserWithRoles (); Sjednica sesije = sessionFactory.openSession (); session.beginTransaction (); Korisnik persistentUser = session.find (User.class, detachedUser.getId ()); session.getTransaction (). commit (); session.close (); thrown.expect (LazyInitializationException.class); System.out.println (persistentUser.getRoles (). Size ()); }

U tom slučaju pokušavamo pristupiti ulogama nakon zatvaranja sesije i, kao rezultat, kôd baca a LazyInitializationException.

4. Kako izbjeći pogrešku

Pogledajmo četiri različita rješenja za prevladavanje pogreške.

4.1. Otvorena sesija u gornjem sloju

Najbolja praksa je otvoriti sesiju u sloju postojanosti, na primjer pomoću DAO uzorka.

Sesiju možemo otvoriti u gornjim slojevima kako bismo na siguran način pristupili povezanim objektima. Na primjer, možemo otvoriti sesiju u Pogled sloj.

Kao rezultat, vidjet ćemo povećanje vremena odgovora, što će utjecati na izvedbu aplikacije.

Ovo je rješenje anti-obrazac u smislu načela razdvajanja zabrinutosti. Osim toga, to može uzrokovati kršenje integriteta podataka i dugotrajne transakcije.

4.2. Uključivanje enable_lazy_load_no_trans Vlasništvo

Ovo svojstvo hibernacije koristi se za deklariranje globalne politike za lijeno učitavanje dohvaćanja objekata.

Prema zadanim postavkama ovo je svojstvo lažno. Uključivanje znači da će svaki pristup pridruženom entitetu lijenog učitavanja biti umotan u novu sesiju koja se izvodi u novoj transakciji:

Koristeći ovo svojstvo za izbjegavanje LazyInitializationException pogreška se ne preporučuje jer će usporiti performanse naše aplikacije. To je zato što ćemo završiti s n + 1 problemom. Jednostavno rečeno, to znači jedan SELECT za Korisnik i N dodatnih SELECT-a za dohvaćanje uloga svakog korisnika.

Ovaj pristup nije učinkovit i također se smatra anti-uzorkom.

4.3. Koristeći FetchType.EAGER Strategija

Ovu strategiju možemo koristiti zajedno s a @OneToMany napomena, na primjer:

@OneToMany (fetch = FetchType.EAGER) @JoinColumn (name = "user_id") private Postavi uloge;

Ovo je vrsta kompromitiranog rješenja za određenu upotrebu kada moramo dohvatiti povezanu kolekciju za većinu naših slučajeva upotrebe.

Dakle, mnogo je lakše proglasiti ŽELJAN tip dohvata umjesto da eksplicitno dohvaća zbirku za većinu različitih poslovnih tokova.

4.4. Korištenje dohvaćanja pridruživanja

Možemo koristiti a PRIDRUŽITE SE PREUZIMANJU direktiva u JPQL za preuzimanje povezane zbirke na zahtjev, na primjer:

SELECT u FROM User u JOIN FETCH u.roles

Ili možemo koristiti API kriterija hibernacije:

Kriteriji kriterija = session.createCriteria (User.class); kriteriji.setFetchMode ("uloge", FetchMode.EAGER);

Ovdje navodimo povezanu kolekciju koju treba dohvatiti iz baze podataka zajedno s Korisnik objekt na istom kružnom putu. Korištenje ovog upita poboljšava učinkovitost ponavljanja jer uklanja potrebu za zasebnim dohvaćanjem povezanih objekata.

Ovo je najučinkovitije i najsitnije rješenje za izbjegavanje LazyInitializationException pogreška.

5. Zaključak

U ovom smo članku vidjeli kako se nositi s org.hibernate.LazyInitializationException: nije moguće inicijalizirati proxy - nema sesije pogreška.

Istražili smo različite pristupe zajedno s problemima izvedbe. Važno je koristiti jednostavno i učinkovito rješenje kako biste izbjegli utjecaj na performanse.

Konačno, vidjeli smo kako je pristup prikupljanju spojeva dobar način da se izbjegne pogreška.

Kao i uvijek, kôd je dostupan na GitHub-u.