Hibernácia nemohla inicializovať proxy server - žiadna relácia

1. Prehľad

Pri práci s režimom dlhodobého spánku sme sa mohli stretnúť s chybou, ktorá hovorí: org.hibernate.LazyInitializationException: nepodarilo sa inicializovať proxy - žiadna relácia.

V tejto rýchlej príručke sa bližšie pozrieme na hlavnú príčinu chyby a naučíme sa jej predchádzať.

2 Pochopenie chyby

Prístup k lenivo načítanému objektu mimo kontextu otvorenej relácie dlhodobého spánku bude mať za následok túto výnimku.

Je dôležité to pochopiť čo je Session, Lenivá inicializácia,a Proxy objekt a ako sa združujú v Hibernácia rámec.

  • Session je kontext vytrvalosti, ktorý predstavuje konverzáciu medzi aplikáciou a databázou
  • Lenivé načítanie znamená, že objekt nebude načítaný do súboru Session kontext, až kým sa k nemu nepristúpi v kóde.
  • Hibernácia vytvára dynamiku Objekt proxy podtrieda, ktorá zasiahne databázu iba pri prvom použití objektu.

Táto chyba znamená, že sa pokúsime načítať objekt lenivého načítania z databázy pomocou objektu proxy, ale relácia dlhodobého spánku je už uzavretá.

3. Príklad pre LazyInitializationException

Pozrime sa na výnimku v konkrétnom scenári.

Chceme vytvoriť jednoduchý Používateľ objekt s pridruženými rolami. Použijme JUnit na demonštráciu LazyInitializationException chyba.

3.1. Hibernate Utility Class

Najskôr definujme a HibernateUtil triedy na vytvorenie a SessionFactory s konfiguráciou.

Použijeme pamäť HSQLDB databázy.

3.2. Subjekty

Tu je náš Používateľ subjekt:

@Entity @Table (name = "user") verejná trieda User {@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 súkromné ​​role; } 

A s tým spojené Rola subjekt:

@Entity @Table (name = "role") verejná trieda Rola {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "id") private int id; @Column (name = "role_name") private String roleName; }

Ako vidíme, existuje vzťah medzi dvoma Používateľ a Rola.

3.3. Vytváranie používateľov s rolami

Ďalej vytvoríme dve Rola objekty:

Rola admin = nová rola („Správca“); Rola dba = nová rola („DBA“);

Potom vytvoríme a Používateľ s rolami:

User user = new User ("Bob", "Smith"); user.addRole (admin); user.addRole (dba);

Na záver môžeme otvoriť reláciu a vydržať objekty:

Relácia session = sessionFactory.openSession (); session.beginTransaction (); user.getRoles (). forEach (role -> session.save (role)); session.save (užívateľ); session.getTransaction (). commit (); session.close ();

3.4. Načítavajú sa role

V prvom scenári uvidíme, ako správne načítať roly používateľov:

@Test public void whenAccessUserRolesInsideSession_thenSuccess () {User detachedUser = createUserWithRoles (); Relácia session = sessionFactory.openSession (); session.beginTransaction (); Používateľ persistentUser = session.find (User.class, detachedUser.getId ()); Assert.assertEquals (2, persistentUser.getRoles (). Size ()); session.getTransaction (). commit (); session.close (); }

Tu pristupujeme k objektu v rámci relácie, preto tu nie je žiadna chyba.

3.5. Načítanie rolí zlyhalo

V druhom scenári budeme nazývať a getRoles metóda mimo relácie:

@Test public void whenAccessUserRolesOutsideSession_thenThrownException () {User detachedUser = createUserWithRoles (); Relácia session = sessionFactory.openSession (); session.beginTransaction (); Používateľ persistentUser = session.find (User.class, detachedUser.getId ()); session.getTransaction (). commit (); session.close (); thrown.expect (LazyInitializationException.class); System.out.println (persistentUser.getRoles (). Size ()); }

V takom prípade sa pokúsime získať prístup k rolám po ukončení relácie a vo výsledku kód hodí a LazyInitializationException.

4. Ako sa vyhnúť chybe

Pozrime sa na štyri rôzne riešenia, ako chybu prekonať.

4.1. Otvorte reláciu v hornej vrstve

Osvedčeným postupom je otvorenie relácie vo vrstve vytrvalosti, napríklad pomocou vzoru DAO.

Reláciu môžeme otvoriť v horných vrstvách, aby sme mohli bezpečne pristupovať k pridruženým objektom. Reláciu môžeme napríklad otvoriť v vyhliadka vrstva.

Vo výsledku zaznamenáme predĺženie doby odozvy, čo ovplyvní výkon aplikácie.

Toto riešenie je anti-vzorom v zmysle princípu oddelenia záujmov. Okrem toho môže spôsobiť narušenie integrity údajov a dlhodobé transakcie.

4.2. Zapínanie enable_lazy_load_no_trans Nehnuteľnosť

Táto vlastnosť dlhodobého spánku sa používa na deklaráciu globálnej politiky pre načítanie objektov s lenivým načítaním.

Štandardne je táto vlastnosť nepravdivé. Zapnutie znamená, že každý prístup k pridruženej lenivo načítanej entite bude zabalený do novej relácie prebiehajúcej v novej transakcii:

Použitie tejto vlastnosti, aby sa zabránilo LazyInitializationException Chyba sa neodporúča, pretože to spomalí výkon našej aplikácie. To preto, lebo budeme skončiť s problémom n + 1. Jednoducho povedané, to znamená jeden SELECT pre Používateľ a N ďalších VÝBEROV na získanie rolí každého používateľa.

Tento prístup nie je efektívny a považuje sa tiež za anti-vzor.

4.3. Použitím FetchType.EAGER Stratégia

Túto stratégiu môžeme použiť spolu s a @OneToMany anotácia, napríklad:

@OneToMany (fetch = FetchType.EAGER) @JoinColumn (name = "user_id") súkromné ​​sady rolí;

Toto je druh kompromitovaného riešenia pre konkrétne použitie, keď potrebujeme načítať priradenú kolekciu pre väčšinu našich prípadov použitia.

Je teda oveľa jednoduchšie deklarovať EAGER typ načítania namiesto explicitného načítania kolekcie pre väčšinu rôznych obchodných tokov.

4.4. Používanie načítania spojenia

Môžeme použiť a PRIPOJTE SA K FETCHU smernica v JPQL načítať súvisiacu zbierku na požiadanie, napríklad:

ZVOLTE u OD OD UŽÍVATEĽA u PRIPOJTE SA K FETU u.roles

Alebo môžeme použiť API Hibernate Criteria API:

Kritériá kritérií = session.createCriteria (User.class); kritéria.setFetchMode ("role", FetchMode.EAGER);

Tu zadáme priradenú kolekciu, ktorá by sa mala načítať z databázy spolu s Používateľ objekt na tej istej spiatočnej ceste. Používanie tohto dotazu zvyšuje efektivitu iterácie, pretože eliminuje potrebu samostatného načítania priradených objektov.

Toto je najefektívnejšie a najjemnejšie riešenie, ktorému sa treba vyhnúť LazyInitializationException chyba.

5. Záver

V tomto článku sme videli, ako sa vysporiadať s org.hibernate.LazyInitializationException: nepodarilo sa inicializovať proxy - žiadna relácia chyba.

Preskúmali sme rôzne prístupy spolu s problémami s výkonom. Je dôležité používať jednoduché a efektívne riešenie, aby nedošlo k ovplyvneniu výkonu.

Nakoniec sme videli, ako je prístup načítavania dobrým spôsobom, ako sa vyhnúť chybe.

Ako vždy, kód je k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found