Hurtigguide til dvalemodus enable_lazy_load_no_trans Property

1. Oversikt

Mens du bruker lat belastning i dvalemodus, kan vi møte unntak og si at det ikke er noen økt.

I denne opplæringen vil vi diskutere hvordan du løser disse problemer med lat belastning. For å gjøre dette bruker vi Spring Boot for å utforske et eksempel.

2. Lazy Loading Issues

Målet med lat lasting er å spare ressurser ved ikke å laste relaterte objekter i minnet når vi laster hovedobjektet. I stedet utsetter vi initialiseringen av late enheter til det øyeblikket de trengs. Dvalemodus bruker fullmakter og innpakningspapirer for å implementere lat lasting.

Når du henter data som er lastet inn, er det to trinn i prosessen. For det første er det å fylle hovedobjektet, og for det andre å hente dataene i dets fullmakter. Lasting av data krever alltid en åpen Økt i dvale.

Problemet oppstår når det andre trinnet skjer etter at transaksjonen er avsluttet, som fører til en LazyInitializationException.

Den anbefalte tilnærmingen er å utforme applikasjonen vår for å sikre at datahenting skjer i en enkelt transaksjon. Men dette kan noen ganger være vanskelig når du bruker en lat enhet i en annen del av koden som ikke klarer å bestemme hva som er eller ikke er lastet.

Dvalemodus har en løsning, en enable_lazy_load_no_trans eiendom. Å slå på dette betyr at hver henting av en lat enhet vil åpne en midlertidig økt og kjøre i en egen transaksjon.

3. Lazy Loading Eksempel

La oss se på oppførselen til lat belastning under noen få scenarier.

3.1 Sett opp enheter og tjenester

Anta at vi har to enheter, Bruker og Dokument. En Bruker kan ha mange Dokuments, og vi bruker @OneToMany for å beskrive det forholdet. Vi bruker også @Fetch (FetchMode.SUBSELECT) for effektivitet.

Vi bør merke oss at som standard @OneToMany har en lat hentetype.

La oss nå definere vår Bruker enhet:

@Entity public class User {// andre felt er utelatt for kortfattethet @OneToMany (mappedBy = "userId") @Fetch (FetchMode.SUBSELECT) privatliste docs = new ArrayList (); }

Deretter trenger vi et tjenestelag med to metoder for å illustrere de forskjellige alternativene. En av dem er merket som @Transaksjonell. Her utfører begge metodene den samme logikken ved å telle alle dokumenter fra alle brukere:

@Service offentlig klasse ServiceLayer {@Autowired private UserRepository userRepository; @Transactional (readOnly = true) offentlig lang countAllDocsTransactional () {return countAllDocs (); } public long countAllDocsNonTransactional () {return countAllDocs (); } private long countAllDocs () {return userRepository.findAll () .stream () .map (User :: getDocs) .mapToLong (Collection :: size) .sum (); }}

La oss nå se nærmere på de følgende tre eksemplene. Vi bruker også SQLStatementCountValidator å forstå effektiviteten til løsningen ved å telle antall henvendelser som er utført.

3.2. Lazy Loading med en omliggende transaksjon

Først av alt, la oss bruke lat lasting på anbefalt måte. Så, vi vil kalle vår @Transaksjonell metode i tjenestelaget:

@Test offentlig ugyldig nårCallTransactionalMethodWithPropertyOff_thenTestPass () {SQLStatementCountValidator.reset (); lang docsCount = serviceLayer.countAllDocsTransactional (); assertEquals (EXPECTED_DOCS_COLLECTION_SIZE, docsCount); SQLStatementCountValidator.assertSelectCount (2); }

Som vi kan se, fungerer dette og resulterer i to rundturer til databasen. Den første turen velger brukere, og den andre velger dokumentene deres.

3.3. Lazy Loading utenfor en transaksjon

La oss kalle en ikke-transaksjonsmetode for å simulere feilen vi får uten en omliggende transaksjon:

@Test (forventet = LazyInitializationException.class) offentlig ugyldig nårCallNonTransactionalMethodWithPropertyOff_thenThrowException () {serviceLayer.countAllDocsNonTransactional (); }

Som spådd, dette resulterer i en feil som getDocs funksjon av Bruker brukes utenfor en transaksjon.

3.4. Lat lasting med automatisk transaksjon

For å fikse det kan vi aktivere eiendommen:

spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true

Når eiendommen er slått på, får vi ikke lenger en LazyInitializationException.

Antall spørringer viser det imidlertid seks rundturer er gjort til databasen. Her velger en rundtur brukere, og fem rundturer velger dokumenter for hver av fem brukere:

@Test offentlig ugyldig når CallNonTransactionalMethodWithPropertyOn_thenGetNplusOne () {SQLStatementCountValidator.reset (); lang docsCount = serviceLayer.countAllDocsNonTransactional (); assertEquals (EXPECTED_DOCS_COLLECTION_SIZE, docsCount); SQLStatementCountValidator.assertSelectCount (EXPECTED_USERS_COUNT + 1); }

Vi har kjørt inn i den beryktede N + 1-utgaven, til tross for at vi setter en hentestrategi for å unngå det!

4. Sammenligning av tilnærminger

La oss kort diskutere fordeler og ulemper.

Når eiendommen er slått på, trenger vi ikke å bekymre deg for transaksjoner og deres grenser. Dvalemodus klarer det for oss.

Imidlertid fungerer løsningen sakte, fordi dvalemodus starter en transaksjon for oss på hver henting.

Det fungerer perfekt for demoer og når vi ikke bryr oss om ytelsesproblemer. Dette kan være greit hvis det brukes til å hente en samling som bare inneholder ett element, eller et enkelt relatert objekt i en til en relasjon.

Uten eiendommen har vi finkornet kontroll over transaksjonene, og vi møter ikke lenger ytelsesproblemer.

Samlet sett er dette ikke en produksjonsklar funksjon, og Hibernate-dokumentasjonen advarer oss:

Selv om aktivering av denne konfigurasjonen kan gjøre LazyInitializationException gå bort, det er bedre å bruke en henteplan som garanterer at alle eiendommer blir riktig initialisert før økten stenges.

5. Konklusjon

I denne opplæringen undersøkte vi å håndtere lat belastning.

Vi prøvde en dvalemodus for å overvinne LazyInitializationException. Vi så også hvordan det reduserer effektiviteten og kan bare være en levedyktig løsning for et begrenset antall brukssaker.

Som alltid er alle kodeeksempler tilgjengelig på GitHub.


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