Beknopte handleiding voor slaapstand enable_lazy_load_no_trans eigenschap

1. Overzicht

Bij het gebruik van lazy loading in Hibernate kunnen we met uitzonderingen te maken krijgen, namelijk dat er geen sessie is.

In deze zelfstudie bespreken we hoe u deze problemen met lazy loading kunt oplossen. Om dit te doen, gebruiken we Spring Boot om een ​​voorbeeld te verkennen.

2. Problemen met lui laden

Het doel van lazy loading is om bronnen te besparen door gerelateerde objecten niet in het geheugen te laden wanneer we het hoofdobject laden. In plaats daarvan stellen we de initialisatie van luie entiteiten uit tot het moment dat ze nodig zijn. Hibernate gebruikt proxy's en verzamelwrappers om lazy loading te implementeren.

Bij het ophalen van lui geladen gegevens zijn er twee stappen in het proces. Ten eerste is er het vullen van het hoofdobject en ten tweede het ophalen van de gegevens binnen zijn proxy's. Het laden van gegevens vereist altijd een open Sessie in Slaapstand.

Het probleem doet zich voor wanneer de tweede stap plaatsvindt nadat de transactie is gesloten, wat leidt tot een LazyInitializationException.

De aanbevolen aanpak is om onze applicatie zo te ontwerpen dat het ophalen van gegevens gebeurt in één enkele transactie. Maar dit kan soms moeilijk zijn bij het gebruik van een luie entiteit in een ander deel van de code die niet kan bepalen wat wel of niet is geladen.

Hibernate heeft een tijdelijke oplossing, een enable_lazy_load_no_trans eigendom. Dit inschakelen betekent dat elke ophaalactie van een luie entiteit zal een tijdelijke sessie openen en lopen binnen een afzonderlijke transactie.

3. Lazy Loading Voorbeeld

Laten we eens kijken naar het gedrag van lazy loading onder een paar scenario's.

3.1 Entiteiten en services instellen

Stel dat we twee entiteiten hebben, Gebruiker en Document. een Gebruiker kan er veel hebben Documents, en we zullen gebruiken @Een te veel om die relatie te beschrijven. We zullen ook gebruiken @Fetch (FetchMode.SUBSELECT) voor efficiëntie.

We moeten er rekening mee houden dat standaard @Een te veel heeft een lui ophaaltype.

Laten we nu onze Gebruiker entiteit:

@Entity public class User {// andere velden zijn weggelaten voor beknoptheid @OneToMany (mappedBy = "userId") @Fetch (FetchMode.SUBSELECT) private List docs = new ArrayList (); }

Vervolgens hebben we een servicelaag nodig met twee methoden om de verschillende opties te illustreren. Een van hen is geannoteerd als @Transactional. Hier voeren beide methoden dezelfde logica uit door alle documenten van alle gebruikers te tellen:

@Service openbare klasse ServiceLayer {@Autowired privé UserRepository userRepository; @Transactional (readOnly = true) openbare lange countAllDocsTransactional () {return countAllDocs (); } openbare lange countAllDocsNonTransactional () {return countAllDocs (); } private long countAllDocs () {return userRepository.findAll () .stream () .map (Gebruiker :: getDocs) .mapToLong (Collectie :: grootte) .sum (); }}

Laten we nu de volgende drie voorbeelden eens nader bekijken. We zullen ook gebruiken SQLStatementCountValidator om de efficiëntie van de oplossing te begrijpen door het aantal uitgevoerde query's te tellen.

3.2. Lazy Loading met een omringende transactie

Laten we allereerst lazy loading op de aanbevolen manier gebruiken. Dus we bellen onze @Transactional methode in de servicelaag:

@Test openbare leegte whenCallTransactionalMethodWithPropertyOff_thenTestPass () {SQLStatementCountValidator.reset (); lang docsCount = serviceLayer.countAllDocsTransactional (); assertEquals (EXPECTED_DOCS_COLLECTION_SIZE, docsCount); SQLStatementCountValidator.assertSelectCount (2); }

Zoals we kunnen zien, werkt dit en resulteert in twee rondreizen naar de database. De eerste rondreis selecteert gebruikers en de tweede selecteert hun documenten.

3.3. Lui laden buiten een transactie

Laten we nu een niet-transactionele methode aanroepen om de fout te simuleren die we krijgen zonder een omringende transactie:

@Test (verwacht = LazyInitializationException.class) openbare leegte whenCallNonTransactionalMethodWithPropertyOff_thenThrowException () {serviceLayer.countAllDocsNonTransactional (); }

Zoals voorspeld, dit resulteert in een fout als de getDocs functie van Gebruiker wordt buiten een transactie gebruikt.

3.4. Lui laden met automatische transactie

Om dit op te lossen, kunnen we de eigenschap inschakelen:

spring.jpa.properties.hibernate.enable_lazy_load_no_trans = waar

Als de woning is ingeschakeld, krijgen we niet langer een LazyInitializationException.

Het aantal zoekopdrachten laat dat echter zien Er zijn zes rondreizen gemaakt naar de database. Hier selecteert één rondreis gebruikers, en vijf rondreizen selecteren documenten voor elk van de vijf gebruikers:

@Test openbare leegte whenCallNonTransactionalMethodWithPropertyOn_thenGetNplusOne () {SQLStatementCountValidator.reset (); lang docsCount = serviceLayer.countAllDocsNonTransactional (); assertEquals (EXPECTED_DOCS_COLLECTION_SIZE, docsCount); SQLStatementCountValidator.assertSelectCount (EXPECTED_USERS_COUNT + 1); }

We zijn het beruchte N + 1-probleem tegengekomen, ondanks het feit dat we een ophaalstrategie hebben ingesteld om dit te vermijden!

4. Vergelijking van de benaderingen

Laten we kort de voor- en nadelen bespreken.

Als de eigenschap is ingeschakeld, hoeven we ons geen zorgen te maken over transacties en hun grenzen. Hibernate regelt dat voor ons.

De oplossing werkt echter traag, omdat Hibernate bij elke ophaalactie een transactie voor ons start.

Het werkt perfect voor demo's en wanneer we ons niet druk maken om prestatieproblemen. Dit kan ok zijn als het wordt gebruikt om een ​​verzameling op te halen die slechts één element bevat, of een enkel gerelateerd object in een een-op-een relatie.

Zonder het eigendom hebben we een fijnmazige controle over de transacties, en we worden niet langer geconfronteerd met prestatieproblemen.

Over het algemeen is dit geen productieklare functie, en de Hibernate-documentatie waarschuwt ons:

Hoewel het inschakelen van deze configuratie kan maken LazyInitializationException ga weg, is het beter om een ​​ophaalplan te gebruiken dat garandeert dat alle eigenschappen correct zijn geïnitialiseerd voordat de sessie wordt gesloten.

5. Conclusie

In deze tutorial hebben we het omgaan met lazy loading onderzocht.

We hebben een Hibernate-eigenschap geprobeerd om het LazyInitializationException. We hebben ook gezien hoe het de efficiëntie vermindert en mogelijk alleen een haalbare oplossing is voor een beperkt aantal gebruiksscenario's.

Zoals altijd zijn alle codevoorbeelden beschikbaar op GitHub.