Hibernate kan proxy niet initialiseren - geen sessie

1. Overzicht

Als we met Hibernate werken, zijn we mogelijk een fout tegengekomen die zegt: org.hibernate.LazyInitializationException: kon proxy niet initialiseren - geen sessie.

In deze korte tutorial gaan we dieper in op de hoofdoorzaak van de fout en leren we hoe je deze kunt vermijden.

2 De fout begrijpen

Toegang tot een lui-geladen object buiten de context van een open Hibernate-sessie zal resulteren in deze uitzondering.

Het is belangrijk om te begrijpen wat is sessie, Luie initialisatie,en Proxy-object en hoe ze samenkomen in de Slaapstand kader.

  • Sessie is een persistentiecontext die een gesprek tussen een applicatie en de database vertegenwoordigt
  • Trage voortgang betekent dat het object niet wordt geladen in de Sessie context totdat het in code wordt benaderd.
  • Hibernate zorgt voor een dynamiek Proxy-object subklasse die de database alleen raakt wanneer we het object voor het eerst gebruiken.

Deze fout betekent dat we proberen een lazy-loaded object uit de database op te halen met behulp van een proxy-object, maar de Hibernate-sessie is al gesloten.

3. Voorbeeld voor LazyInitializationException

Laten we de uitzondering eens bekijken in een concreet scenario.

We willen een eenvoudig maken Gebruiker object met bijbehorende rollen. Laten we JUnit gebruiken om het LazyInitializationException fout.

3.1. Hibernate Utility Class

Laten we eerst een SlaapstandUtil class om een SessionFactory met configuratie.

We zullen het in-memory gebruiken HSQLDB database.

3.2. Entiteiten

Hier is onze Gebruiker entiteit :

@Entity @Table (naam = "gebruiker") openbare klasse Gebruiker {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) @Column (naam = "id") privé int id; @Column (name = "first_name") private String firstName; @Column (name = "last_name") private String lastName; @OneToMany privé Stel rollen in; } 

En de bijbehorende Rol entiteit :

@Entity @Table (naam = "rol") openbare klasse Rol {@Id @GeneratedValue (strategie = GenerationType.IDENTITY) @Column (naam = "id") privé int id; @Column (name = "role_name") private String roleName; }

Zoals we kunnen zien, is er een een-op-veel-relatie tussen Gebruiker en Rol.

3.3. Gebruiker met rollen aanmaken

Laten we er vervolgens twee maken Rol voorwerpen :

Rol admin = nieuwe rol ("Admin"); Rol dba = nieuwe rol ("DBA");

Vervolgens maken we een Gebruiker met de rollen:

Gebruiker gebruiker = nieuwe gebruiker ("Bob", "Smith"); user.addRole (beheerder); user.addRole (dba);

Eindelijk kunnen we een sessie openen en de objecten behouden:

Sessiesessie = sessionFactory.openSession (); session.beginTransaction (); user.getRoles (). forEach (rol -> session.save (rol)); session.save (gebruiker); session.getTransaction (). commit (); session.close ();

3.4. Rollen ophalen

In het eerste scenario zullen we zien hoe u gebruikersrollen op de juiste manier kunt ophalen:

@Test openbare leegte whenAccessUserRolesInsideSession_thenSuccess () {Gebruiker detachedUser = createUserWithRoles (); Sessiesessie = sessionFactory.openSession (); session.beginTransaction (); Gebruiker persistentUser = session.find (User.class, detachedUser.getId ()); Assert.assertEquals (2, persistentUser.getRoles (). Size ()); session.getTransaction (). commit (); session.close (); }

Hier hebben we toegang tot het object in de sessie, daarom is er geen fout.

3.5. Ophalen van rollen mislukt

In het tweede scenario noemen we een getRoles methode buiten de sessie:

@Test openbare leegte whenAccessUserRolesOutsideSession_thenThrownException () {Gebruiker detachedUser = createUserWithRoles (); Sessiesessie = sessionFactory.openSession (); session.beginTransaction (); Gebruiker persistentUser = session.find (User.class, detachedUser.getId ()); session.getTransaction (). commit (); session.close (); gegooid.expect (LazyInitializationException.class); System.out.println (persistentUser.getRoles (). Size ()); }

In dat geval proberen we toegang te krijgen tot de rollen nadat de sessie is gesloten, en als resultaat gooit de code een LazyInitializationException.

4. Hoe u de fout kunt vermijden

Laten we eens kijken naar vier verschillende oplossingen om de fout te verhelpen.

4.1. Open sessie in bovenste laag

U kunt het beste een sessie openen in de persistentielaag, bijvoorbeeld met behulp van het DAO-patroon.

We kunnen de sessie openen in de bovenste lagen om op een veilige manier toegang te krijgen tot de bijbehorende objecten. We kunnen de sessie bijvoorbeeld openen in het Visie laag.

Als gevolg hiervan zullen we een langere reactietijd zien, wat de prestaties van de applicatie zal beïnvloeden.

Deze oplossing is een antipatroon in termen van het Separation of Concerns-principe. Bovendien kan het leiden tot schendingen van de gegevensintegriteit en langdurige transacties.

4.2. Aanzetten enable_lazy_load_no_trans Eigendom

Deze Hibernate-eigenschap wordt gebruikt om een ​​globaal beleid aan te geven voor het ophalen van lazy-loaded objecten.

Deze eigenschap is standaard false. Als u dit inschakelt, betekent dit dat elke toegang tot een bijbehorende lazy-loaded entiteit wordt verpakt in een nieuwe sessie die wordt uitgevoerd in een nieuwe transactie:

Gebruik deze eigenschap om te vermijden LazyInitializationException fout wordt niet aanbevolen omdat het de prestaties van onze applicatie zal vertragen. Dit komt omdat we eindigen met een n + 1 probleem. Simpel gezegd, dat betekent één SELECT voor de Gebruiker en N extra SELECTs om de rollen van elke gebruiker op te halen.

Deze benadering is niet efficiënt en wordt ook beschouwd als een antipatroon.

4.3. Gebruik makend van FetchType.EAGER Strategie

We kunnen deze strategie gebruiken in combinatie met een @Een te veel annotatie, bijvoorbeeld:

@OneToMany (fetch = FetchType.EAGER) @JoinColumn (name = "user_id") privé Set rollen;

Dit is een soort gecompromitteerde oplossing voor een bepaald gebruik wanneer we de bijbehorende verzameling voor de meeste van onze gebruiksscenario's moeten ophalen.

Het is dus veel gemakkelijker om de EAGER fetch-type in plaats van de collectie expliciet op te halen voor de meeste verschillende bedrijfsstromen.

4.4. Samen met ophalen gebruiken

We kunnen een WORD LID VAN FETCH richtlijn in JPQL om de bijbehorende collectie op aanvraag op te halen, bijvoorbeeld:

SELECTEER u UIT Gebruiker u WORD LID VAN FETCH u.roles

Of we kunnen de Hibernate Criteria API gebruiken:

Criteria criteria = session.createCriteria (User.class); criteria.setFetchMode ("rollen", FetchMode.EAGER);

Hier specificeren we de bijbehorende collectie die samen met de Gebruiker object op dezelfde rondreis. Het gebruik van deze query verbetert de efficiëntie van iteratie, aangezien het de noodzaak om de bijbehorende objecten afzonderlijk op te halen, overbodig maakt.

Dit is de meest efficiënte en fijnmazige oplossing om de LazyInitializationException fout.

5. Conclusie

In dit artikel hebben we gezien hoe we om moeten gaan met de org.hibernate.LazyInitializationException: kon proxy niet initialiseren - geen sessie fout.

We hebben verschillende benaderingen en prestatieproblemen onderzocht. Het is belangrijk om een ​​eenvoudige en efficiënte oplossing te gebruiken om de prestaties niet te beïnvloeden.

Ten slotte hebben we gezien hoe de benadering voor het ophalen van joins een goede manier is om de fout te vermijden.

Zoals altijd is de code beschikbaar op GitHub.


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