Hurtig vejledning til dvale-aktiveret_lazy_load_no_trans-ejendom

1. Oversigt

Mens du bruger doven indlæsning i dvale, står vi muligvis over for undtagelser og siger, at der ikke er nogen session.

I denne vejledning diskuterer vi, hvordan du løser disse dovne belastningsproblemer. For at gøre dette bruger vi Spring Boot til at udforske et eksempel.

2. Lazy Loading-problemer

Målet med doven indlæsning er at spare ressourcer ved ikke at indlæse relaterede objekter i hukommelsen, når vi indlæser hovedobjektet. I stedet udsætter vi initialiseringen af ​​dovne enheder til det øjeblik, de er nødvendige. Dvale bruger fuldmagter og indsamlingsindpakninger til at implementere doven belastning.

Når du henter dovenligt indlæste data, er der to trin i processen. For det første udfyldes hovedobjektet, og for det andet hentes dataene inden for dens proxyer. Indlæsning af data kræver altid en åben Session i dvale.

Problemet opstår, når det andet trin sker, efter at transaktionen er afsluttet, hvilket fører til en LazyInitializationException.

Den anbefalede tilgang er at designe vores applikation for at sikre, at datahentning sker i en enkelt transaktion. Men dette kan nogle gange være svært, når du bruger en doven enhed i en anden del af koden, der ikke er i stand til at bestemme, hvad der er eller ikke er blevet indlæst.

Dvaletilstand har en løsning, en enable_lazy_load_no_trans ejendom. At tænde dette betyder det hver hentning af en doven enhed åbner en midlertidig session og køre inde i en separat transaktion.

3. Lazy Loading Eksempel

Lad os se på adfærd ved doven belastning under et par scenarier.

3.1 Opsætning af enheder og tjenester

Antag, at vi har to enheder, Bruger og Dokument. En Bruger kan have mange Dokuments, og vi bruger @OneToMany at beskrive det forhold. Vi bruger også @Fetch (FetchMode.SUBSELECT) for effektivitet.

Vi skal bemærke, at som standard @OneToMany har en doven hentetype.

Lad os nu definere vores Bruger enhed:

@Entity public class-bruger {// andre felter udelades for kortfattethed @OneToMany (mappedBy = "userId") @Fetch (FetchMode.SUBSELECT) privat liste docs = ny ArrayList (); }

Dernæst har vi brug for et servicelag med to metoder til at illustrere de forskellige muligheder. En af dem er kommenteret som @Transaktionel. Her udfører begge metoder den samme logik ved at tælle alle dokumenter fra alle brugere:

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

Lad os nu se nærmere på de følgende tre eksempler. Vi bruger også SQLStatementCountValidator at forstå effektiviteten af ​​løsningen ved at tælle antallet af udførte forespørgsler.

3.2. Lazy Loading med en omgivende transaktion

Lad os først og fremmest bruge doven belastning på den anbefalede måde. Så vi kalder vores @Transaktionel metode i servicelaget:

@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 rundrejser til databasen. Den første rundtur vælger brugere, og den anden vælger deres dokumenter.

3.3. Lazy Loading uden for en transaktion

Lad os nu kalde en ikke-transaktionsmetode for at simulere den fejl, vi får uden en omgivende transaktion:

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

Som forudsagt, dette resulterer i en fejl som den getDocs funktion af Bruger bruges uden for en transaktion.

3.4. Lazy Loading med automatisk transaktion

For at løse det kan vi aktivere ejendommen:

spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true

Når ejendommen er tændt, får vi ikke længere en LazyInitializationException.

Men antallet af forespørgsler viser det der er foretaget seks rundrejser til databasen. Her vælger en rundtur brugere og fem returrejser vælger dokumenter til hver af fem brugere:

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

Vi er stødt på den berygtede N + 1-udgave, på trods af at vi har sat en hentestrategi for at undgå det!

4. Sammenligning af fremgangsmåderne

Lad os kort diskutere fordele og ulemper.

Når ejendommen er tændt, behøver vi ikke bekymre os om transaktioner og deres grænser. Dvaletilstand klarer det for os.

Løsningen fungerer dog langsomt, fordi Hibernate starter en transaktion for os ved hver hentning.

Det fungerer perfekt til demoer, og når vi ikke er interesserede i præstationsproblemer. Dette kan være ok, hvis det bruges til at hente en samling, der kun indeholder et element eller et enkelt relateret objekt i en til en relation.

Uden ejendommen har vi finkornet kontrol over transaktionerne, og vi står ikke længere over for præstationsproblemer.

Samlet set er dette ikke en produktionsklar funktion, og dvale-dokumentationen advarer os:

Selvom aktivering af denne konfiguration kan gøre LazyInitializationException gå væk, det er bedre at bruge en henteplan, der garanterer, at alle egenskaber initialiseres korrekt, før sessionen lukkes.

5. Konklusion

I denne vejledning udforskede vi håndteringen af ​​doven belastning.

Vi prøvede en dvaleegenskab for at hjælpe med at overvinde LazyInitializationException. Vi så også, hvordan det reducerer effektiviteten og måske kun er en levedygtig løsning til et begrænset antal brugssager.

Som altid er alle kodeeksempler tilgængelige på GitHub.


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