Kratki vodič za hibernaciju svojstvo enable_lazy_load_no_trans

1. Pregled

Dok koristimo lijeno učitavanje u hibernaciji, mogli bismo se suočiti s iznimkama, rekavši da nema sesije.

U ovom uputstvu razgovarat ćemo o rješavanju ovih lijenih problema s učitavanjem. Da bismo to učinili, upotrijebit ćemo Spring Boot da istražimo primjer.

2. Problemi s lijenim učitavanjem

Cilj lijenog učitavanja je ušteda resursa ne učitavanjem povezanih objekata u memoriju kada učitavamo glavni objekt. Umjesto toga, odgađamo inicijalizaciju lijenih entiteta do trenutka kada su potrebni. Hibernate koristi proxyje i omote za prikupljanje kako bi primijenio lijeno učitavanje.

Prilikom dohvaćanja lijeno učitanih podataka u postupku su dva koraka. Prvo, tu je popunjavanje glavnog objekta, i drugo, preuzimanje podataka unutar njegovih proxyja. Za učitavanje podataka uvijek je potrebno otvoreno Sjednica u hibernaciji.

Problem nastaje kada se drugi korak dogodi nakon zatvaranja transakcije, što dovodi do a LazyInitializationException.

Preporučeni pristup je dizajniranje naše aplikacije kako bi se osiguralo da se preuzimanje podataka događa u jednoj transakciji. Ali, to ponekad može biti teško kada koristite lijeni entitet u drugom dijelu koda koji nije u stanju utvrditi što je ili nije učitano.

Hibernate ima zaobilazno rješenje, enable_lazy_load_no_trans imovine. Uključivanje ovoga znači ono svaki dohvat lijenog entiteta otvorit će privremenu sesiju i pokrenuti unutar zasebne transakcije.

3. Primjer lijenog učitavanja

Pogledajmo ponašanje lijenog utovara u nekoliko scenarija.

3.1 Postavljanje entiteta i usluga

Pretpostavimo da imamo dva entiteta, Korisnik i Dokument. Jedan Korisnik mogu imati mnogo Dokuments, i mi ćemo koristiti @OneToMany za opisivanje te veze. Također, mi ćemo koristiti @Fetch (FetchMode.SUBSELECT) radi učinkovitosti.

Trebali bismo napomenuti da, prema zadanim postavkama, @OneToMany ima lijeni tip dohvaćanja.

Ajmo sada definirati naše Korisnik entitet:

Korisnik javne klase @Entity {// ostala polja izostavljena su zbog kratkoće @OneToMany (mappedBy = "userId") @Fetch (FetchMode.SUBSELECT) privatni popis docs = new ArrayList (); }

Dalje, trebamo servisni sloj s dvije metode za ilustraciju različitih opcija. Jedan od njih je označen kao @Transational. Ovdje obje metode izvode istu logiku brojeći sve dokumente od svih korisnika:

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

Pogledajmo sada bliže sljedeća tri primjera. Također ćemo koristiti SQLStatementCountValidator kako bi se razumjela učinkovitost rješenja, brojeći broj izvršenih upita.

3.2. Lijeno učitavanje s okolnom transakcijom

Prije svega, upotrijebimo lijeno utovar na preporučeni način. Dakle, nazvat ćemo naše @Transational metoda u sloju usluge:

@Test public void whenCallTransactionalMethodWithPropertyOff_thenTestPass () {SQLStatementCountValidator.reset (); long docsCount = serviceLayer.countAllDocsTransactional (); assertEquals (EXPECTED_DOCS_COLLECTION_SIZE, docsCount); SQLStatementCountValidator.assertSelectCount (2); }

Kao što vidimo, ovo djeluje i rezultira u dva povratna putovanja do baze podataka. Prvo putovanje odabire korisnike, a drugo odabire njihove dokumente.

3.3. Lijeno učitavanje izvan transakcije

Nazovimo sada ne-transakcijsku metodu da simuliramo grešku koju dobijemo bez okolne transakcije:

@Test (očekuje se = LazyInitializationException.class) javna praznina kadaCallNonTransactionalMethodWithPropertyOff_thenThrowException () {serviceLayer.countAllDocsNonTransactional (); }

Kao što je i predviđeno, ovo rezultira pogreškom kao getDocs funkcija Korisnik koristi se izvan transakcije.

3.4. Lijeno učitavanje uz automatsku transakciju

Da bismo to popravili, možemo omogućiti svojstvo:

spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true

S uključenom imovinom više ne dobivamo LazyInitializationException.

Međutim, broj upita pokazuje to do baze podataka izvršeno je šest povratnih putovanja. Ovdje jedno povratno putovanje odabire korisnike, a pet povratnih putovanja odabire dokumente za svakog od pet korisnika:

@Test public void whenCallNonTransactionalMethodWithPropertyOn_thenGetNplusOne () {SQLStatementCountValidator.reset (); long docsCount = serviceLayer.countAllDocsNonTransactional (); assertEquals (EXPECTED_DOCS_COLLECTION_SIZE, docsCount); SQLStatementCountValidator.assertSelectCount (EXPECTED_USERS_COUNT + 1); }

Naišli smo na ozloglašeno izdanje N + 1, unatoč činjenici da smo postavili strategiju dohvaćanja kako bismo je izbjegli!

4. Usporedba pristupa

Razgovarajmo ukratko o prednostima i nedostacima.

Kad je imovina uključena, ne moramo se brinuti o transakcijama i njihovim granicama. Hibernate nam to uspijeva.

Međutim, rješenje djeluje polako, jer Hibernate započinje transakciju za nas pri svakom dohvaćanju.

Savršeno funkcionira za demonstracije i kada nas nije briga za probleme s izvedbom. To može biti u redu ako se koristi za dohvaćanje kolekcije koja sadrži samo jedan element ili jedan srodni objekt u odnosu jedan prema jedan.

Bez imovine imamo preciznu kontrolu transakcija, i više se ne suočavamo s problemima izvedbe.

Sve u svemu, ovo nije značajka spremna za proizvodnju, a hibernacijska dokumentacija nas upozorava:

Iako omogućavanje ove konfiguracije može učiniti LazyInitializationException odlazite, bolje je koristiti plan dohvata koji jamči da su sva svojstva pravilno inicijalizirana prije zatvaranja sesije.

5. Zaključak

U ovom uputstvu istražili smo kako se baviti lijenim opterećenjem.

Isprobali smo hibernacijsko svojstvo kako bismo prevladali LazyInitializationException. Također smo vidjeli kako smanjuje učinkovitost i može biti održivo rješenje samo za ograničeni broj slučajeva korištenja.

Kao i uvijek, svi primjeri koda dostupni su na GitHub-u.


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