A hibernálás nem tudta inicializálni a proxyt - nincs munkamenet
1. Áttekintés
A hibernált rendszerrel együttműködve előfordulhat, hogy hibát észleltünk: org.hibernate.LazyInitializationException: nem lehet inicializálni a proxyt - nincs munkamenet.
Ebben a gyors bemutatóban közelebbről megvizsgáljuk a hiba kiváltó okát, és megtanuljuk, hogyan kerülhetjük el azt.
2 A hiba megértése
A lustán letöltött objektumokhoz való hozzáférés a nyitott hibernált munkamenet környezetén kívül eredményezi ezt a kivételt.
Fontos megérteni mi az a Session, Lusta inicializálás,és Proxy Object és hogyan jönnek össze a Hibernálás keretrendszer.
- Ülés olyan perzisztencia-kontextus, amely egy alkalmazás és az adatbázis közötti beszélgetést ábrázolja
- Lusta betöltés azt jelenti, hogy az objektum nem lesz betöltve a Ülés kontextusban, amíg hozzá nem férnek hozzá kódban.
- A hibernálás dinamikát hoz létre Proxy Object alosztály, amely csak akkor éri el az adatbázist, amikor először használjuk az objektumot.
Ez a hiba azt jelenti, hogy egy lusta módon feltöltött objektumot proxyobjektummal próbálunk lekérni, de a Hibernate munkamenet már le van zárva.
3. Példa erre LazyInitializationException
Nézzük meg a kivételt egy konkrét forgatókönyvben.
Egyszerűt akarunk létrehozni Felhasználó objektum társított szerepekkel. Használjuk a JUnit-et a LazyInitializationException hiba.
3.1. Hibernált segédprogram
Először definiáljuk a HibernateUtil osztály létrehozásához SessionFactory konfigurációval.
Használjuk a memóriát HSQLDB adatbázis.
3.2. Entitások
Itt van a miénk Felhasználó entitás:
@Entity @Table (name = "user") public class User {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "id") private int id; @ Oszlop (név = "keresztnév") privát karakterlánc keresztnév; @ Oszlop (név = "vezetéknév") privát karakterlánc vezetéknév; @OneToMany private Set szerepkörök; }
És a kapcsolódó Szerep entitás:
@Entity @Table (name = "role") public class Role {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "id") private int id; @ Oszlop (név = "szerep_név") privát karakterlánc szerepNév; }
Mint láthatjuk, van egy a sokhoz viszony Felhasználó és Szerep.
3.3. Felhasználó létrehozása szerepkörökkel
Ezután hozzunk létre kettőt Szerep tárgyak:
Role admin = new Role ("Rendszergazda"); Szerep: dba = új szerep ("DBA");
Ezután létrehozunk egy Felhasználó szerepekkel:
Felhasználó felhasználó = új felhasználó ("Bob", "Smith"); user.addRole (admin); user.addRole (dba);
Végül megnyithatunk egy munkamenetet és megtarthatjuk az objektumokat:
Munkamenet = sessionFactory.openSession (); session.beginTransaction (); user.getRoles (). forEach (szerepkör -> session.save (szerep)); session.save (felhasználó); session.getTransaction (). kötelezettség (); session.close ();
3.4. Szerepek beolvasása
Az első esetben meglátjuk, hogyan lehet megfelelő módon lekérni a felhasználói szerepeket:
@Test public void whenAccessUserRolesInsideSession_thenSuccess () {User detachedUser = createUserWithRoles (); Munkamenet = sessionFactory.openSession (); session.beginTransaction (); User persistentUser = session.find (Felhasználó.osztály, detachedUser.getId ()); Assert.assertEquals (2, persistentUser.getRoles (). Size ()); session.getTransaction (). kötelezettség (); session.close (); }
Itt elérjük az objektumot a munkameneten belül, ezért nincs hiba.
3.5. Szerepek meghiúsulása
A második forgatókönyvben a-t hívjuk getRoles módszer a munkameneten kívül:
@Test public void whenAccessUserRolesOutsideSession_thenThrownException () {User detachedUser = createUserWithRoles (); Munkamenet = sessionFactory.openSession (); session.beginTransaction (); User persistentUser = session.find (Felhasználó.osztály, detachedUser.getId ()); session.getTransaction (). kötelezettség (); session.close (); thrown.expect (LazyInitializationException.class); System.out.println (persistentUser.getRoles (). Size ()); }
Ebben az esetben megpróbálunk hozzáférni a szerepkörökhöz a munkamenet lezárása után, és ennek eredményeként a kód dob egy LazyInitializationException.
4. Hogyan lehet elkerülni a hibát
Vizsgáljunk meg négy különböző megoldást a hiba leküzdésére.
4.1. Nyitott munkamenet a felső rétegben
A legjobb gyakorlat egy munkamenet megnyitása a perzisztencia rétegben, például a DAO minta használatával.
Meg tudjuk nyitni a munkamenetet a felső rétegekben, hogy biztonságos módon hozzáférjünk a társított objektumokhoz. Megnyithatjuk például a munkamenetet a Kilátás réteg.
Ennek eredményeként megnő a válaszidő, ami hatással lesz az alkalmazás teljesítményére.
Ez a megoldás antiminta az aggodalmak elkülönítésének elvét tekintve. Ezenkívül az adatok integritásának megsértését és hosszan tartó tranzakciókat is okozhat.
4.2. Bekapcsolni enable_lazy_load_no_trans Ingatlan
Ez a hibernált tulajdonság a lusta betöltött objektumok lekérésére vonatkozó globális házirend deklarálására szolgál.
Alapértelmezés szerint ez a tulajdonság hamis. Bekapcsolása azt jelenti, hogy a társított, lustán töltött entitásokhoz való minden hozzáférés új munkamenetbe kerül, amely új tranzakcióban fut:
Ennek a tulajdonságnak az elkerülése érdekében LazyInitializationException hiba nem ajánlott, mert ez lelassítja alkalmazásunk teljesítményét. Ez azért van, mert megtesszük végül n + 1 problémával jár. Egyszerűen fogalmazva, ez azt jelenti, hogy egy SELECT a Felhasználó és N további SELECTS az egyes felhasználók szerepeinek lekéréséhez.
Ez a megközelítés nem hatékony, és anti-mintának is tekinthető.
4.3. Használata FetchType.EAGER Stratégia
Használhatjuk ezt a stratégiát az a-val együtt @Egy a sokhoz kommentár, például:
@OneToMany (fetch = FetchType.EAGER) @JoinColumn (name = "user_id") privát Szerepkörök beállítása;
Ez egyfajta veszélyeztetett megoldás egy adott felhasználásra, amikor a legtöbb használati esetre be kell szereznünk a társított gyűjteményt.
Tehát sokkal könnyebb kijelenteni a MOHÓ letöltési típus ahelyett, hogy a különféle üzleti folyamatok többségére kifejezetten lekérné a gyűjteményt.
4.4. A Csatlakozás lehívása használatával
Használhatjuk a CSATLAKOZZA FETCH irányelv JPQL igény szerint lekérheti a társított gyűjteményt, például:
KIVÁLASZTÁS UTÓL a felhasználótól u CSATLAKOZZ FETCH u.szerepek
Vagy használhatjuk a Hibernate Criteria API-t:
Feltételek kritériumai = session.createCriteria (Felhasználó.osztály); feltételek.setFetchMode ("szerepkörök", FetchMode.EAGER);
Itt adjuk meg a társított gyűjteményt, amelyet be kell tölteni az adatbázisból, a Felhasználó objektum ugyanazon a körúton. A lekérdezés használata javítja az iteráció hatékonyságát, mivel így nincs szükség a társított objektumok külön-külön történő letöltésére.
Ez a leghatékonyabb és finom szemcseméretű megoldás a LazyInitializationException hiba.
5. Következtetés
Ebben a cikkben láttuk, hogyan kell kezelni a org.hibernate.LazyInitializationException: nem lehet inicializálni a proxyt - nincs munkamenet hiba.
Különböző megközelítéseket tártunk fel a teljesítményproblémákkal együtt. Fontos, hogy egyszerű és hatékony megoldást alkalmazzon a teljesítmény befolyásolásának elkerülése érdekében.
Végül láttuk, hogy a csatlakozás-lehívás megközelítés jó módszer a hibák elkerülésére.
Mint mindig, a kód elérhető a GitHubon.