Hibernate predmemorija druge razine

1. Pregled

Jedna od prednosti slojeva apstrakcije baze podataka poput ORM (objektno-relacijsko mapiranje) okvira je njihova sposobnost transparentnog predmemoriranja podataka preuzeto iz temeljne trgovine. To pomaže u uklanjanju troškova pristupa bazama podataka za podatke kojima se često pristupa.

Poboljšanje izvedbe može biti značajno ako su omjeri čitanja / pisanja predmemoriranog sadržaja visoki, posebno za entitete koji se sastoje od velikih grafova objekata.

U ovom članku istražujemo Hibernate predmemoriju druge razine.

Objašnjavamo neke osnovne pojmove i kao i uvijek sve ilustriramo jednostavnim primjerima. Koristimo JPA i vraćamo se na Hibernate native API samo za one značajke koje nisu standardizirane u JPA.

2. Što je predmemorija druge razine?

Kao i većina ostalih potpuno opremljenih ORM okvira, Hibernate ima koncept predmemorije prve razine. To je predmemorija s opsegom sesije koja osigurava da se svaka instanca entiteta učita samo jednom u trajnom kontekstu.

Nakon zatvaranja sesije prekida se i predmemorija prve razine. To je zapravo poželjno, jer omogućava istodobnim sesijama rad s instancama entiteta međusobno izolirano.

S druge strane, predmemorija druge razine jest SjednicaTvornica-scoped, što znači da ga dijele sve sesije stvorene u istoj tvornici sesija. Kada se instanca entiteta traži prema njezinom id-u (bilo logikom aplikacije ili internim hibernacijom, npr. kada učitava asocijacije na taj entitet iz drugih entiteta) i ako je za taj entitet omogućeno predmemoriranje druge razine, događa se sljedeće:

  • Ako je instanca već prisutna u predmemoriji prve razine, ona se odatle vraća
  • Ako instanca nije pronađena u predmemoriji prve razine, a odgovarajuće stanje instance predmemorirano je u predmemoriji druge razine, tada se podaci preuzimaju odatle i instanca se okuplja i vraća
  • U suprotnom, potrebni se podaci učitavaju iz baze podataka, a instanca se sastavlja i vraća

Jednom kada je instanca spremljena u kontekst trajnosti (predmemorija prve razine), ona se odatle vraća u svim sljedećim pozivima unutar iste sesije dok se sesija ne zatvori ili se instanca ručno izbaci iz konteksta trajnosti. Također, učitano stanje instance pohranjuje se u L2 predmemoriju ako već nije bilo tamo.

3. Tvornica regije

Hibernate predmemoriranje druge razine osmišljeno je tako da ne zna stvarnog davatelja usluge predmemorije. Hibernate samo treba imati provedbu org.hibernate.cache.spi.RegionFactory sučelje koje obuhvaća sve pojedinosti specifične za stvarne pružatelje predmemorije. U osnovi, djeluje kao most između hibernacije i pružatelja predmemorije.

U ovom članku koristimo Ehcache kao pružatelja predmemorije, koji je zrela i široko korištena predmemorija. Naravno, možete odabrati bilo kojeg drugog davatelja usluge, sve dok postoji implementacija a RegijaTvornica za to.

Klasnoj stazi dodajemo implementaciju tvornice regije Ehcache sa sljedećom Mavenovom ovisnošću:

 org.hibernate hibernate-ehcache 5.2.2.Finalni 

Ovdje pogledajte najnoviju verziju hibernacija-ehcache. Međutim, pobrinite se za to hibernacija-ehcache verzija je jednaka hibernaciji koju koristite u projektu, npr. ako koristite hibernate-ehcache 5.2.2.Završni kao u ovom primjeru, tada bi trebala biti i verzija Hibernate 5.2.2.Finalna.

The hibernacija-ehcache artefakt ovisi o samoj implementaciji Ehcachea, koji je tako također tranzitivno uključen u put predavanja.

4. Omogućavanje keširanja na drugoj razini

Sa sljedeća dva svojstva Hibernatu kažemo da je L2 predmemoriranje omogućeno i dajemo mu ime tvorničke klase regije:

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

Na primjer, u postojanost.xml to bi izgledalo kao:

 ...   ... 

Samo onemogućite predmemoriranje druge razine (na primjer u svrhe otklanjanja pogrešaka) hibernate.cache.use_second_level_cache svojstvo na lažno.

5. Izrada entiteta koji se može predmemorirati

Da bi učiniti entitet prihvatljivim za predmemoriranje druge razine, bilježimo ga hibernacijom @ org.hibernate.annotations.cache napomena i navedite strategiju istodobnosti predmemorije.

Neki programeri smatraju da je dobro dodati standard @ javax.persistence.Cacheable napomena (također, iako Hibernate ne zahtijeva), pa bi implementacija klase entiteta mogla izgledati ovako:

@Entity @Cacheable @ org.hibernate.annotations.Cache (usage = CacheConcurrencyStrategy.READ_WRITE) javna klasa Foo {@Id @GeneratedValue (strategy = GenerationType.AUTO) @Column (name = "ID") private long id; @Column (name = "NAME") ime privatnog niza; // geteri i postavljači}

Za svaku klasu entiteta Hibernate će koristiti zasebnu regiju predmemorije za pohranu stanja primjeraka za tu klasu. Naziv regije je potpuno kvalificirani naziv klase.

Na primjer, Foo instance se pohranjuju u predmemoriju s imenom com.baeldung.hibernate.cache.model.Foo u Ehcacheu.

Da bismo provjerili radi li predmemoriranje, možemo napisati brzi test poput ovog:

Foo foo = novi 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 (veličina, veća od nego (0));

Ovdje koristimo Ehcache API izravno da to potvrdimo com.baeldung.hibernate.cache.model.Foo predmemorija nije prazna nakon što učitamo a Foo primjer.

Također možete omogućiti evidentiranje SQL-a generiranog hibernacijom i pozivanje fooService.findOne (foo.getId ()) više puta u testu kako bi provjerili je li Odaberi izjava za utovar Foo ispisuje se samo jednom (prvi put), što znači da se u sljedećim pozivima instanca entiteta preuzima iz predmemorije.

6. Strategija istodobnosti predmemorije

Na temelju slučajeva korištenja, slobodno možemo odabrati jednu od sljedećih strategija istodobnosti predmemorije:

  • SAMO ZA ČITANJE: Koristi se samo za entitete koji se nikad ne mijenjaju (izuzetak se izbacuje ako se pokuša ažurirati takav entitet). Vrlo je jednostavan i izvedljiv. Vrlo pogodno za neke statičke referentne podatke koji se ne mijenjaju
  • NONSTRICT_READ_WRITE: Predmemorija se ažurira nakon što je izvršena transakcija koja je promijenila zahvaćene podatke. Dakle, jaka dosljednost nije zajamčena i postoji mali vremenski okvir u kojem se iz predmemorije mogu dobiti zastarjeli podaci. Ova vrsta strategije prikladna je za slučajeve upotrebe koji mogu tolerirati eventualnu dosljednost
  • PROČITAJ_PIŠI: Ova strategija jamči snažnu dosljednost koju postiže korištenjem "mekih" brava: Kada se ažurira predmemorirani entitet, softverska brava se sprema u predmemoriju i za taj entitet, a koja se oslobađa nakon izvršavanja transakcije. Sve istodobne transakcije koje pristupaju zaključanim unosima dohvatit će odgovarajuće podatke izravno iz baze podataka
  • TRANSAKCIONALNI: Izmjene predmemorije vrše se u distribuiranim XA transakcijama. Promjena u predmemoriranom entitetu je ili izvršena ili vraćena u bazu podataka i u predmemoriju u istoj XA transakciji

7. Upravljanje predmemorijom

Ako politike isteka i deložacije nisu definirane, predmemorija bi mogla rasti unedogled i na kraju potrošiti svu dostupnu memoriju. U većini slučajeva Hibernate takve dužnosti upravljanja predmemorijom prepušta pružateljima predmemorije, jer su doista specifične za svaku implementaciju predmemorije.

Na primjer, mogli bismo definirati sljedeću konfiguraciju Ehcachea kako bismo ograničili maksimalan broj predmemoriranih podataka Foo primjeraka do 1000:

8. Predmemorija zbirke

Zbirke se prema predmemoriji ne predmemoriraju i moramo ih izričito označiti kao predmemorirane. Na primjer:

@Entity @Cacheable @ org.hibernate.annotations.Cache (usage = CacheConcurrencyStrategy.READ_WRITE) javna klasa Foo {... @Cacheable @ org.hibernate.annotations.Cache (usage = CacheConcurrencyStrategy.READ_WRITE) @ Collection barSoft; // geteri i postavljači}

9. Interno predstavljanje predmemorirane države

Entiteti se ne pohranjuju u predmemoriju druge razine kao Java instance, već u rastavljenom (hidratiziranom) stanju:

  • Id (primarni ključ) nije pohranjen (pohranjen je kao dio ključa predmemorije)
  • Privremena svojstva se ne pohranjuju
  • Zbirke se ne pohranjuju (za detalje pogledajte dolje)
  • Vrijednosti svojstva koje nisu udružene pohranjuju se u izvornom obliku
  • Pohranjuje se samo id (strani ključ) za ToOne udruge

Ovo prikazuje općeniti hibernate dizajn druge razine predmemorije u kojem model predmemorije odražava temeljni relacijski model, koji je prostorno učinkovit i olakšava sinkronizaciju.

9.1. Interno predstavljanje predmemoriranih zbirki

Već smo spomenuli da moramo izričito naznačiti da zbirka (OneToMany ili ManyToMany Association) može se predmemorirati, inače se ne sprema u predmemoriju.

Zapravo, Hibernate pohranjuje zbirke u odvojena predmemorijska područja, po jednu za svaku zbirku. Naziv regije potpuno je kvalificirani naziv klase plus naziv svojstva zbirke, na primjer: com.baeldung.hibernate.cache.model.Foo.bars. To nam daje fleksibilnost da definiramo zasebne parametre predmemorije za zbirke, npr. politika iseljenja / isteka.

Također je važno napomenuti da se za svaki unos zbirke keširaju samo ID-ovi entiteta sadržanih u zbirci, što znači da je u većini slučajeva dobro sadržane entitete učiniti i predmemoriranim.

10. Invalidiranje predmemorije za upite u HQL DML stilu i izvorne upite

Što se tiče HQL-a u DML stilu (umetnuti, ažuriranje i izbrisati HQL izjave), Hibernate je u stanju utvrditi na koje entitete utječu takve operacije:

entityManager.createQuery ("update Foo set ... where ..."). executeUpdate ();

U ovom se slučaju sve instance Fooa izbacuju iz L2 predmemorije, dok ostali predmemorirani sadržaj ostaje nepromijenjen.

Međutim, kada su u pitanju izvorni SQL DML izrazi, Hibernate ne može pogoditi što se ažurira, pa onesposobljava cijelu predmemoriju druge razine:

session.createNativeQuery ("ažuriraj FOO postavljen ... gdje ..."). executeUpdate ();

Ovo vjerojatno nije ono što želite! Rješenje je reći Hibernateu na koje entitete utječu izvorni DML izrazi, tako da može izbaciti samo unose koji se odnose na Foo entiteti:

Upit nativeQuery = entityManager.createNativeQuery ("ažuriraj FOO postavljen ... gdje ..."); nativeQuery.unwrap (org.hibernate.SQLQuery.class) .addSynchronizedEntityClass (Foo.class); nativeQuery.executeUpdate ();

Previše smo se vratili u Hibernate native SQLQuery API, jer ova značajka (još nije) definirana u JPA.

Imajte na umu da se gore odnosi samo na DML izjave (umetnuti, ažuriranje, izbrisati i izvorni pozivi funkcije / postupka). Native Odaberi upiti ne onemogućuju predmemoriju.

11. Predmemorija upita

Rezultati HQL upita također se mogu predmemorirati. Ovo je korisno ako često izvršavate upit za entitete koji se rijetko mijenjaju.

Da biste omogućili predmemoriju upita, postavite vrijednost hibernate.cache.use_query_cache svojstvo da pravi:

hibernate.cache.use_query_cache = true

Zatim, za svaki upit morate izričito naznačiti da je upit moguće predmemorirati (putem org.hibernate.cacheable savjet za upit):

entityManager.createQuery ("odaberite f iz Foo f") .setHint ("org.hibernate.cacheable", tačno) .getResultList ();

11.1. Pitajte najbolje primjere predmemorije

Evo nekoliko smjernice i najbolje prakse povezane s predmemoriranjem upita:

  • Kao što je slučaj sa zbirkama, samo se id-ovi entiteta vraćenih kao rezultat upita koji se može predmemorirati, pa se toplo preporučuje da je za takve entitete omogućena predmemorija druge razine.
  • Za svaki upit postoji jedan unos predmemorije po svakoj kombinaciji kombinacija vrijednosti parametara (vezane varijable), tako da upiti za koje očekujete puno različitih kombinacija vrijednosti parametara nisu dobri kandidati za predmemoriranje.
  • Upiti koji uključuju klase entiteta za koje postoje česte promjene u bazi podataka također nisu dobri kandidati za predmemoriranje, jer će se onesposobiti kad god dođe do promjene koja se odnosi na bilo koji entitet klasificiran koji sudjeluje u upitu, bez obzira jesu li promijenjene instance predmemorirano kao dio rezultata upita ili ne.
  • Prema zadanim postavkama svi rezultati predmemorije upita pohranjuju se u org.hibernate.cache.internal.StandardQueryCache regija. Kao i kod predmemoriranja entiteta / zbirke, možete prilagoditi parametre predmemorije za ovu regiju kako biste definirali politike deložacije i isteka prema vašim potrebama. Za svaki upit također možete odrediti prilagođeni naziv regije kako biste pružili različite postavke za različite upite.
  • Za sve tablice koje su upitane kao dio upita koji se mogu predmemorirati, Hibernate zadržava vremenske oznake zadnjeg ažuriranja u zasebnoj regiji s imenom org.hibernate.cache.spi.UpdateTimestampsCache. Biti svjestan ove regije vrlo je važno ako koristite predmemoriranje upita, jer Hibernate njime provjerava da rezultati predmemoriranog upita nisu ustajali. Unosi u ovoj predmemoriji ne smiju se deložirati / isteći sve dok postoje predmemorirani rezultati upita za odgovarajuće tablice u regijama rezultata upita. Najbolje je isključiti automatsko iseljavanje i istek za ovu regiju predmemorije, jer ionako ne troši puno memorije.

12. Zaključak

U ovom smo članku pogledali kako postaviti Hibernate predmemoriju druge razine. Uvidjeli smo da je prilično jednostavno konfigurirati i koristiti, jer Hibernate čini sve teško dizajniranje iza kulisa čineći upotrebu predmemorije druge razine transparentnom za poslovnu logiku aplikacija.

Implementacija ovog vodiča za predmemoriranje druge razine hibernacije dostupna je na Githubu. Ovo je projekt zasnovan na Mavenu, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.