Dvaletilstand kunne ikke initialisere proxy - ingen session

1. Oversigt

Når vi arbejder med dvale, har vi muligvis stødt på en fejl, der siger: org.hibernate.LazyInitializationException: kunne ikke initialisere proxy - ingen session.

I denne hurtige vejledning vil vi se nærmere på årsagen til fejlen og lære at undgå den.

2 Forstå fejlen

Adgang til et dovet belastet objekt uden for rammerne af en åben dvale-session vil resultere i denne undtagelse.

Det er vigtigt at forstå hvad er session, Lazy Initialisation,og Proxy-objekt og hvordan de kommer sammen i Dvale ramme.

  • Session er en vedholdenhedskontekst, der repræsenterer en samtale mellem et program og databasen
  • Lazy Loading betyder, at objektet ikke vil blive lastet til Session sammenhæng, indtil den åbnes i kode.
  • Dvaletilstand skaber en dynamik Proxy-objekt underklasse, der kun rammer databasen, når vi først bruger objektet.

Denne fejl betyder, at vi forsøger at hente et doven-belastet objekt fra databasen ved hjælp af et proxy-objekt, men dvale-sessionen er allerede lukket.

3. Eksempel på LazyInitializationException

Lad os se undtagelsen i et konkret scenario.

Vi ønsker at skabe et simpelt Bruger objekt med tilknyttede roller. Lad os bruge JUnit til at demonstrere LazyInitializationException fejl.

3.1. Dvaletilstandsklasse

Lad os først definere en Dvaletilstand klasse for at oprette en SessionFactory med konfiguration.

Vi bruger in-memory HSQLDB database.

3.2. Enheder

Her er vores Bruger enhed :

@Entity @Table (name = "user") offentlig klasse bruger {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) @Column (name = "id") privat int id; @Column (name = "first_name") privat streng fornavn; @Column (name = "last_name") privat streng efternavn; @OneToMany private sæt roller; } 

Og det tilknyttede Rolle enhed :

@Entity @Table (name = "role") public class Role {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "id") privat int id; @Kolonne (navn = "rollenavn") privat streng rollenavn; }

Som vi kan se, er der en-til-mange-forbindelse mellem Bruger og Rolle.

3.3. Oprettelse af bruger med roller

Lad os derefter oprette to Rolle genstande:

Rolleadministrator = ny rolle ("Admin"); Rolle dba = ny rolle ("DBA");

Derefter opretter vi en Bruger med rollerne:

Brugerbruger = ny bruger ("Bob", "Smith"); user.addRole (admin); user.addRole (dba);

Endelig kan vi åbne en session og fastholde objekterne:

Session session = sessionFactory.openSession (); session.beginTransaction (); user.getRoles (). forEach (rolle -> session.save (rolle)); session.save (bruger); session.getTransaction (). commit (); session.close ();

3.4. Henter roller

I det første scenarie ser vi, hvordan man henter brugerroller på en korrekt måde:

@Test offentlig ugyldig nårAccessUserRolesInsideSession_thenSuccess () {Bruger detachedUser = createUserWithRoles (); Session session = sessionFactory.openSession (); session.beginTransaction (); Bruger persistentUser = session.find (User.class, detachedUser.getId ()); Assert.assertEquals (2, persistentUser.getRoles (). Størrelse ()); session.getTransaction (). commit (); session.close (); }

Her får vi adgang til objektet inde i sessionen, derfor er der ingen fejl.

3.5. Henter rollefejl

I det andet scenario kalder vi en getRoles metode uden for sessionen:

@Test offentlig ugyldig nårAccessUserRolesOutsideSession_thenThrownException () {User detachedUser = createUserWithRoles (); Session session = sessionFactory.openSession (); session.beginTransaction (); Bruger persistentUser = session.find (User.class, detachedUser.getId ()); session.getTransaction (). commit (); session.close (); throw.expect (LazyInitializationException.class); System.out.println (persistentUser.getRoles (). Størrelse ()); }

I så fald forsøger vi at få adgang til rollerne efter sessionen blev lukket, og som et resultat kaster koden a LazyInitializationException.

4. Sådan undgår du fejlen

Lad os se på fire forskellige løsninger for at overvinde fejlen.

4.1. Åbn session i øverste lag

Den bedste praksis er at åbne en session i persistenslaget, for eksempel ved hjælp af DAO-mønsteret.

Vi kan åbne sessionen i de øverste lag for at få adgang til de tilknyttede objekter på en sikker måde. For eksempel kan vi åbne sessionen i Udsigt lag.

Som et resultat vil vi se en stigning i svartid, hvilket vil påvirke applikationens ydeevne.

Denne løsning er et antimønster med hensyn til princippet om adskillelse af bekymringer. Derudover kan det forårsage overtrædelser af dataintegritet og langvarige transaktioner.

4.2. Tænder enable_lazy_load_no_trans Ejendom

Denne dvaleegenskab bruges til at erklære en global politik til hentning af dovne objekter.

Denne egenskab er som standard falsk. At tænde det betyder, at hver adgang til en tilknyttet dovenindlæst enhed vil blive pakket i en ny session, der kører i en ny transaktion:

Brug af denne egenskab for at undgå LazyInitializationException fejl anbefales ikke, da det vil sænke vores applikations ydeevne. Dette er fordi vi ender med et n + 1-problem. Kort sagt, det betyder en VÆLG for Bruger og N yderligere SELECTs for at hente hver brugers roller.

Denne tilgang er ikke effektiv og betragtes også som et antimønster.

4.3. Ved brug af FetchType.EAGER Strategi

Vi kan bruge denne strategi sammen med en @OneToMany kommentar, for eksempel:

@OneToMany (fetch = FetchType.EAGER) @JoinColumn (name = "user_id") private Sæt roller;

Dette er en slags kompromitteret løsning til en bestemt anvendelse, når vi har brug for at hente den tilknyttede samling i de fleste af vores brugssager.

Så det er meget lettere at erklære IVRIGE hentetype i stedet for eksplicit at hente samlingen til de fleste af de forskellige forretningsstrømme.

4.4. Brug af Join Fetching

Vi kan bruge en Bliv medlem direktiv i JPQL at hente den tilknyttede samling on-demand, for eksempel:

VÆLG u FRA Bruger u JOIN FETCH u.roles

Eller vi kan bruge Hibernate Criteria API:

Kriteriekriterier = session.createCriteria (User.class); criteria.setFetchMode ("roller", FetchMode.EAGER);

Her specificerer vi den tilknyttede samling, der skal hentes fra databasen sammen med Bruger objekt på samme returflyvning. Brug af denne forespørgsel forbedrer effektiviteten af ​​iteration, da det eliminerer behovet for at hente de tilknyttede objekter separat.

Dette er den mest effektive og finkornede løsning for at undgå LazyInitializationException fejl.

5. Konklusion

I denne artikel så vi, hvordan vi skulle håndtere org.hibernate.LazyInitializationException: kunne ikke initialisere proxy - ingen session fejl.

Vi udforskede forskellige tilgange sammen med præstationsproblemer. Det er vigtigt at bruge en enkel og effektiv løsning for at undgå at påvirke ydeevnen.

Endelig så vi, hvordan tilslutningsmetoden er en god måde at undgå fejlen på.

Som altid er koden tilgængelig på GitHub.