Dvalemodus på andre nivå

1. Oversikt

En av fordelene med databaseabstraksjonslag som ORM-rammer (object-relational mapping) er deres muligheten til å lagre data transparent hentet fra den underliggende butikken. Dette bidrar til å eliminere databasetilgangskostnader for ofte tilgjengelige data.

Ytelsesgevinster kan være betydelige hvis lese- / skriveforholdene for hurtigbufret innhold er høye, spesielt for enheter som består av store objektgrafer.

I denne artikkelen utforsker vi dvalemodus på andre nivå i dvalemodus.

Vi forklarer noen grunnleggende begreper, og som alltid illustrerer vi alt med enkle eksempler. Vi bruker JPA og faller tilbake til Hibernate native API bare for de funksjonene som ikke er standardisert i JPA.

2. Hva er en andre nivå cache?

Som de fleste andre fullt utstyrte ORM-rammer, har Hibernate konseptet med første nivå cache. Det er en økt-hurtigbuffer som sikrer at hver enhetsforekomst bare lastes inn en gang i vedvarende sammenheng.

Når økten er lukket, avsluttes også hurtignivå på første nivå. Dette er faktisk ønskelig, ettersom det gjør det mulig for samtidige økter å jobbe med enhetsforekomster isolert fra hverandre.

På den annen side er cache på andre nivå SessionFactory-scoped, noe som betyr at den deles av alle økter opprettet med samme øktfabrikk. Når en enhetsforekomst blir slått opp av sin id (enten etter applikasjonslogikk eller av dvalemodus internt, f.eks. når den laster tilknytninger til den enheten fra andre enheter), og hvis hurtigbufring på andre nivå er aktivert for den enheten, skjer følgende:

  • Hvis en forekomst allerede er tilstede i hurtignivåbufferen, returneres den derfra
  • Hvis en forekomst ikke blir funnet i cache på første nivå, og den tilsvarende forekomsttilstanden er hurtigbufret i cache på andre nivå, blir dataene hentet derfra og en forekomst blir samlet og returnert
  • Ellers lastes de nødvendige dataene fra databasen, og en forekomst blir samlet og returnert

Når forekomsten er lagret i utholdenhetskontekst (hurtigbuffer på første nivå), returneres den derfra i alle påfølgende anrop i samme økt til økten lukkes eller forekomsten blir manuelt kastet ut fra utholdenhetskonteksten. Dessuten er den lastede forekomsttilstanden lagret i L2-hurtigbuffer hvis den ikke allerede var der.

3. Regionfabrikk

Dvale på andre nivå caching er designet for å være uvitende om den faktiske cache-leverandøren som brukes. Dvalemodus trenger bare å få en implementering av org.hibernate.cache.spi.RegionFactory grensesnitt som innkapsler alle detaljer som er spesifikke for faktiske cache-leverandører. I utgangspunktet fungerer det som en bro mellom dvalemodus og cache-leverandører.

I denne artikkelen vi bruker Ehcache som cache-leverandør, som er en moden og mye brukt hurtigbuffer. Du kan selvfølgelig velge hvilken som helst annen leverandør, så lenge det er en implementering av en RegionFactory for det.

Vi legger til implementeringen av Ehcache-regionfabrikken til klassestien med følgende Maven-avhengighet:

 org. dvale dvalemodus-ehcache 5.2.2.Final 

Ta en titt her for den nyeste versjonen av dvale-ehcache. Men sørg for at dvale-ehcache versjonen er lik dvalemodusversjonen som du bruker i prosjektet ditt, f.eks. hvis du bruker dvalemodus-ehcache 5.2.2.Final som i dette eksemplet, bør også versjonen av dvalemodus være 5.2.2.Final.

De dvale-ehcache artefakt har en avhengighet av selve Ehcache-implementeringen, som dermed også inngår transitt i klassestien.

4. Aktivering av mellomnivå caching

Med de to følgende egenskapene forteller vi dvalemodus at L2-bufring er aktivert, og vi gir den navnet på regionfabrikklassen:

hibernate.cache.use_second_level_cache = ekte hibernate.cache.region.factory_class = org.hibernate.cache.ehcache.EhCacheRegionFactory 

For eksempel i persistence.xml det ser ut som:

 ...   ... 

For å deaktivere hurtigbufring på andre nivå (for eksempel for feilsøking), er det bare å stille hibernate.cache.use_second_level_cache eiendom til falsk.

5. Gjøre en enhet cacheable

For å gjøre en enhet kvalifisert for hurtigbufring på andre nivå, vi kommenterer det med dvalemodus @ org.hibernate.annotations.Cache kommentar og spesifiser en cache-samtidighetsstrategi.

Noen utviklere anser at det er en god konvensjon å legge til standarden @ javax.persistence.Cacheable kommentar også (selv om det ikke kreves av dvalemodus), så en implementering av enhetsklassen kan se slik ut:

@Entity @Cacheable @ org.hibernate.annotations.Cache (bruk = CacheConcurrencyStrategy.READ_WRITE) offentlig klasse Foo {@Id @GeneratedValue (strategi = GenerationType.AUTO) @Column (navn = "ID") privat lang id; @Column (name = "NAME") privat strengnavn; // getters og setters}

For hver enhetsklasse vil dvalemodus bruke en egen hurtigbufferegion til å lagre forekomster for den klassen. Regionnavnet er det fullt kvalifiserte klassenavnet.

For eksempel, Foo forekomster lagres i en hurtigbuffer som heter com.baeldung.hibernate.cache.model.Foo i Ehcache.

For å bekrefte at caching fungerer, kan vi skrive en rask test som dette:

Foo foo = ny Foo (); fooService.create (foo); fooService.findOne (foo.getId ()); int size = CacheManager.ALL_CACHE_MANAGERS.get (0) .getCache ("com.baeldung.hibernate.cache.model.Foo"). getSize (); assertThat (størrelse, størreThan (0));

Her bruker vi Ehcache API direkte for å bekrefte det com.baeldung.hibernate.cache.model.Foo hurtigbufferen er ikke tom etter at vi har lastet inn a Foo forekomst.

Du kan også aktivere logging av SQL generert av dvalemodus og påkalle fooService.findOne (foo.getId ()) flere ganger i testen for å bekrefte at å velge uttalelse for lasting Foo skrives ut bare én gang (første gang), noe som betyr at enhetsforekomsten i etterfølgende anrop hentes fra hurtigbufferen.

6. Cache-samtidighetsstrategi

Basert på brukstilfeller, står vi fritt til å velge en av følgende strategier for cache-samtidighet:

  • LES_BARE: Brukes bare for enheter som aldri endres (unntak kastes hvis det gjøres et forsøk på å oppdatere en slik enhet). Det er veldig enkelt og performant. Veldig bra for noen statiske referansedata som ikke endres
  • NONSTRICT_READ_WRITE: Cache oppdateres etter at en transaksjon som har endret de berørte dataene er begått. Dermed er ikke sterk konsistens garantert, og det er et lite tidsvindu der foreldede data kan fås fra hurtigbufferen. Denne typen strategi er egnet for brukstilfeller som tåler eventuell konsistens
  • LES SKRIV: Denne strategien garanterer sterk konsistens som den oppnår ved å bruke 'myke' låser: Når en hurtigbufret enhet oppdateres, lagres en myk lås i hurtigbufferen for den enheten også, som frigjøres etter at transaksjonen er begått. Alle samtidige transaksjoner som får tilgang til mykt låste oppføringer, henter tilsvarende data direkte fra databasen
  • TRANSAKSJONELL: Cache-endringer gjøres i distribuerte XA-transaksjoner. En endring i en hurtigbufret enhet er enten forpliktet eller rullet tilbake i både database og cache i samme XA-transaksjon

7. Cache Management

Hvis retningslinjer for utløp og utkastelse ikke er definert, kan hurtigbufferen vokse på ubestemt tid og til slutt forbruke alt tilgjengelig minne. I de fleste tilfeller overlater dvalemodus cacheadministrasjonsoppgaver som disse til cache-leverandører, da de faktisk er spesifikke for hver cacheimplementering.

For eksempel kan vi definere følgende Ehcache-konfigurasjon for å begrense maksimalt antall hurtigbufrede Foo forekomster til 1000:

8. Samlingsbuffer

Samlinger er ikke hurtigbufrede som standard, og vi må eksplisitt merke dem som cacheable. For eksempel:

@Entity @Cacheable @ org.hibernate.annotations.Cache (bruk = CacheConcurrencyStrategy.READ_WRITE) offentlig klasse Foo {... @Cacheable @ org.hibernate.annotations.Cache (bruk = CacheConcurrencyStrategy.READ_WRITE) @OneToMany private samling barer; // getters og setters}

9. Intern representasjon for hurtigbufret stat

Enheter lagres ikke i cache på andre nivå som Java-forekomster, men snarere i demontert (hydrert) tilstand:

  • Id (primærnøkkel) lagres ikke (den lagres som en del av hurtignøkkelen)
  • Forbigående egenskaper lagres ikke
  • Samlinger lagres ikke (se nedenfor for mer informasjon)
  • Eiendomsverdier som ikke er tilknyttet, lagres i sin opprinnelige form
  • Bare id (fremmed nøkkel) er lagret for Til en foreninger

Dette viser generell dvalemodus på andre nivå i cache-design der cachemodellen gjenspeiler den underliggende relasjonsmodellen, som er plasseffektiv og gjør det enkelt å holde de to synkroniserte.

9.1. Intern representasjon av hurtigbufrede samlinger

Vi nevnte allerede at vi eksplisitt må indikere at en samling (OneToMany eller Mange tilknytning) kan caches, ellers lagres den ikke.

Faktisk lagrer dvalemodus samlinger i separate hurtigbufferegioner, en for hver samling. Regionnavnet er et fullt kvalifisert klassenavn pluss navnet på samleeiendommen, for eksempel: com.baeldung.hibernate.cache.model.Foo.barer. Dette gir oss fleksibilitet til å definere separate hurtigbufferparametere for samlinger, f.eks. bortkastelse / utløpspolicy.

Det er også viktig å nevne at bare ID-er for enheter som er inneholdt i en samling blir lagret for hver samlingsoppføring, noe som betyr at det i de fleste tilfeller er en god ide å gjøre de inneholdte enhetene også cacheable.

10. Cache-ugyldighet for HQL DML-stil-spørsmål og innfødte spørsmål

Når det gjelder DML-stil HQL (sett inn, Oppdater og slett HQL-uttalelser), er dvalemodus i stand til å bestemme hvilke enheter som påvirkes av slike operasjoner:

entityManager.createQuery ("oppdater Foo sett ... hvor ..."). executeUpdate ();

I dette tilfellet blir alle Foo-forekomster kastet ut fra L2-hurtigbufferen, mens annet hurtigbufret innhold forblir uendret.

Men når det gjelder innfødte SQL DML-setninger, kan ikke dvalemodus ikke gjette hva som oppdateres, så det ugyldiggjør hele cache på andre nivå:

session.createNativeQuery ("oppdater FOO-sett ... hvor ..."). executeUpdate ();

Dette er sannsynligvis ikke det du vil! Løsningen er å fortelle dvalemodus hvilke enheter som påvirkes av innfødte DML-uttalelser, slik at den bare kan kaste ut oppføringer relatert til Foo enheter:

Query nativeQuery = entityManager.createNativeQuery ("oppdater FOO-sett ... hvor ..."); nativeQuery.unwrap (org.hibernate.SQLQuery.class) .addSynchronizedEntityClass (Foo.class); nativeQuery.executeUpdate ();

Vi har også falt tilbake til dvalemodus SQLQuery API, da denne funksjonen ikke (ennå) er definert i JPA.

Merk at ovenstående bare gjelder DML-uttalelser (sett inn, Oppdater, slett og innfødte funksjon / prosedyreanrop). Innfødt å velge spørsmål ugyldiggjør ikke hurtigbufferen.

11. Spørringsbuffer

Resultater av HQL-spørsmål kan også bufres. Dette er nyttig hvis du ofte utfører et spørsmål på enheter som sjelden endres.

For å aktivere hurtigbuffer, angi verdien på hibernate.cache.use_query_cache eiendom til ekte:

hibernate.cache.use_query_cache = sant

Deretter må du eksplisitt indikere at spørringen kan caches (via en org.hibernate.cacheable spørsmålstips):

entityManager.createQuery ("velg f fra Foo f") .setHint ("org.hibernate.cacheable", true) .getResultList ();

11.1. Beste fremgangsmåter for spørringsbuffer

Her er noen retningslinjer og beste praksis relatert til hurtigbufring:

  • Som det er tilfelle med samlinger, blir bare ID-er for enheter som returneres som et resultat av et søk som kan caches, bufret, så det anbefales sterkt at cache på andre nivå er aktivert for slike enheter.
  • Det er en cacheoppføring per hver kombinasjon av spørringsparameterverdier (bindvariabler) for hvert søk, så spørsmål du forventer mange forskjellige kombinasjoner av parameterverdier for, er ikke gode kandidater for hurtigbufring.
  • Spørringer som involverer enhetsklasser som det er hyppige endringer i databasen for, er heller ikke gode kandidater for hurtigbufring fordi de blir ugyldiggjort hver gang det er en endring relatert til en av enhetene som er klassifisert som deltar i spørringen, uansett om de endrede forekomster bufret som en del av søkeresultatet eller ikke.
  • Som standard lagres alle spørringsbufferesultatene i org.hibernate.cache.internal.StandardQueryCache region. Som med hurtigbufring av enheter / samlinger, kan du tilpasse hurtigbufferparametere for denne regionen for å definere retningslinjer for utkastelse og utløp etter dine behov. For hvert søk kan du også spesifisere et egendefinert regionnavn for å gi forskjellige innstillinger for forskjellige spørsmål.
  • For alle tabeller som blir spurt som en del av spørsmål som kan caches, holder dvalemodus tidsstempler for siste oppdatering i en egen region som heter org.hibernate.cache.spi.UpdateTimestampsCache. Å være klar over denne regionen er veldig viktig hvis du bruker hurtigbufring av spørsmål, fordi dvalemodus bruker den til å bekrefte at bufrede søkeresultater ikke er foreldede. Oppføringene i denne hurtigbufferen må ikke kastes ut / utløpt så lenge det er hurtigbufrede søkeresultater for de tilsvarende tabellene i søkeresultatregioner. Det er best å slå av automatisk utkasting og utløp for denne hurtigbufferegionen, da den uansett ikke bruker mye minne.

12. Konklusjon

I denne artikkelen så vi på hvordan du setter opp dvalemodus på andre nivå i dvalemodus. Vi så at det er ganske enkelt å konfigurere og bruke, ettersom dvalemodus gjør alle de tunge løftene bak kulissene, noe som gjør bruk av andre nivå cache gjennomsiktig for applikasjonslogikken.

Implementeringen av denne Hibernate Second-Level Cache Tutorial er tilgjengelig på Github. Dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.