Dynamisk kartlegging med dvalemodus

1. Introduksjon

I denne artikkelen vil vi utforske noen dynamiske kartleggingsfunksjoner i dvalemodus med @Formel, @Hvor, @Filter og @Noen kommentarer.

Merk at selv om dvalemodus implementerer JPA-spesifikasjonen, er merknader som er beskrevet her bare tilgjengelige i dvalemodus og er ikke direkte bærbare til andre JPA-implementeringer.

2. Prosjektoppsett

For å demonstrere funksjonene trenger vi bare dvalemodus-biblioteket og en H2-database med støtte:

 org.hibernate hibernate-core 5.4.12.Final com.h2database h2 1.4.194 

For den nåværende versjonen av dvale-kjerne bibliotek, gå over til Maven Central.

3. Beregnede kolonner med @Formel

Anta at vi vil beregne en enhetsfeltverdi basert på noen andre egenskaper. En måte å gjøre det på ville være ved å definere et beregnet skrivebeskyttet felt i vår Java-enhet:

@Entity offentlig klasse Medarbeidere implementerer Serialiserbar {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat Heltall-id; private lange bruttoinntekter; privat int skatt InPercents; offentlig lang getTaxJavaWay () {return grossIncome * taxInPercents / 100; }}

Den åpenbare ulempen er at vi må gjøre omberegningen hver gang vi får tilgang til dette virtuelle feltet av getter.

Det ville være mye lettere å få den allerede beregnede verdien fra databasen. Dette kan gjøres med @Formel kommentar:

@Entity offentlig klasse Medarbeidere implementerer Serialiserbar {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat Heltall-id; privat lang bruttoinntekt; private int taxInPercents; @Formula ("grossIncome * taxInPercents / 100") privat lang skatt; }

Med @Formel, kan vi bruke underspørringer, ringe innfødte databasefunksjoner og lagrede prosedyrer og i utgangspunktet gjøre alt som ikke bryter syntaksen til en SQL-valgklausul for dette feltet.

Dvalemodus er smart nok til å analysere SQL vi ga og sette inn riktige tabell- og feltaliaser. Advarselen å være oppmerksom på er at siden verdien av kommentaren er rå SQL, kan den gjøre kartleggingsdatabasen vår avhengig.

Husk også på det verdien beregnes når enheten hentes fra databasen. Derfor, når vi vedvarer eller oppdaterer enheten, vil ikke verdien bli beregnet på nytt før enheten blir kastet ut av konteksten og lastet på nytt:

Ansatt ansatt = ny ansatt (10_000L, 25); session.save (ansatt); session.flush (); session.clear (); ansatt = session.get (ansatt.klasse, ansatt.getId ()); assertThat (medarbeider.getTax ()). erEqualTo (2_500L);

4. Filtrering av enheter med @Hvor

Anta at vi vil gi en tilleggsbetingelse til spørringen når vi ber om en enhet.

For eksempel må vi implementere “soft delete”. Dette betyr at enheten aldri blir slettet fra databasen, men bare merket som slettet med en boolsk felt.

Vi må være nøye med alle eksisterende og fremtidige spørsmål i applikasjonen. Vi må oppgi denne tilleggsvilkåret til alle spørsmål. Heldigvis gir dvalemodus en måte å gjøre dette på ett sted:

@Entity @Where (clause = "delete = false") offentlig klasse Ansatt implementerer Serialiserbar {// ...}

De @Hvor kommentar på en metode inneholder en SQL-ledd som vil bli lagt til i alle spørsmål eller underspørringer til denne enheten:

ansatte.setDeleted (sant); session.flush (); session.clear (); ansatt = session.find (ansatt.klasse, ansatt.getId ()); assertThat (ansatt) .isNull ();

Som i tilfelle @Formel kommentar, siden vi har med rå SQL å gjøre, @Hvor tilstanden blir ikke vurdert på nytt før vi skyller enheten til databasen og kaster den ut av konteksten.

Inntil den tiden vil enheten være i konteksten og være tilgjengelig med spørsmål og oppslag av id.

De @Hvor merknader kan også brukes til et samlingsfelt. Anta at vi har en liste over slettbare telefoner:

@Entity offentlig klasse Telefon implementerer Serializable {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat Heltall-id; privat boolsk slettet; privat strengnummer; }

Så, fra Ansatt side kan vi kartlegge en samling slettbare telefoner som følger:

offentlig klasse Ansatt implementerer Serialiserbar {// ... @OneToMany @JoinColumn (navn = "ansatt_id") @Where (paragraf = "slettet = falsk") privat Sett telefoner = nytt HashSet (0); }

Forskjellen er at Ansatte. Telefoner samlingen vil alltid bli filtrert, men vi kan fortsatt få alle telefoner, inkludert slettede, via direkte spørring:

ansatte.getPhones (). iterator (). neste (). setDeleted (true); session.flush (); session.clear (); ansatt = session.find (ansatt.klasse, ansatt.getId ()); assertThat (medarbeider.getPhones ()). har størrelse (1); Liste fullPhoneList = session.createQuery ("fra telefon"). GetResultList (); assertThat (fullPhoneList) .hasSize (2);

5. Parameterisert filtrering med @Filter

Problemet med @Hvor kommentar er at den lar oss bare spesifisere et statisk spørsmål uten parametere, og det kan ikke deaktiveres eller aktiveres av etterspørsel.

De @Filter kommentar fungerer på samme måte som @Hvor, men det kan også aktiveres eller deaktiveres på øktnivå, og også parametriseres.

5.1. Definere @Filter

For å demonstrere hvordan @Filter fungerer, la oss først legge til følgende filterdefinisjon i Ansatt enhet:

@FilterDef (name = "incomeLevelFilter", parameters = @ParamDef (name = "incomeLimit", type = "int")) @Filter (name = "incomeLevelFilter", condition = "grossIncome>: incomeLimit") offentlig klasse Ansatt implementerer Serialiserbar {

De @FilterDef kommentar definerer filternavnet og et sett med parametere som skal delta i spørringen. Parametertypen er navnet på en av dvale-typene (Type, UserType eller CompositeUserType), i vårt tilfelle en int.

@FilterDef merknader kan plasseres enten på typen eller på pakkenivå. Merk at den ikke spesifiserer selve filtertilstanden (selv om vi kunne spesifisere defaultCondition parameter).

Dette betyr at vi kan definere filteret (dets navn og sett med parametere) på ett sted og deretter definere forholdene for filteret på flere andre steder forskjellig.

Dette kan gjøres med @Filter kommentar. I vårt tilfelle legger vi det i samme klasse for enkelhets skyld. Syntaksen til tilstanden er en rå SQL med parameternavn foran kolon.

5.2. Få tilgang til filtrerte enheter

En annen forskjell på @Filter fra @Hvor er det @Filter er ikke aktivert som standard. Vi må aktivere det på øktnivået manuelt, og oppgi parameterverdiene for det:

session.enableFilter ("incomeLevelFilter") .setParameter ("incomeLimit", 11_000);

Anta at vi har følgende tre ansatte i databasen:

session.save (ny ansatt (10_000, 25)); session.save (ny ansatt (12_000, 25)); session.save (ny ansatt (15_000, 25));

Så når filteret er aktivert, som vist ovenfor, vil bare to av dem være synlige ved å spørre:

Liste over ansatte = session.createQuery ("fra ansatt") .getResultList (); hevder at (ansatte) .hasSize (2);

Merk at både det aktiverte filteret og dets parameterverdier bare brukes i den gjeldende økten. I en ny økt uten filter aktivert, ser vi alle de tre ansatte:

økt = HibernateUtil.getSessionFactory (). openSession (); ansatte = session.createQuery ("fra ansatt"). getResultList (); hevder at (ansatte) .hasSize (3);

Når filteret ikke hentes direkte etter id, blir ikke filteret brukt:

Ansatt ansatt = session.get (ansatt.klasse, 1); assertThat (medarbeider.getGrossinnkomst ()). er likeverdig (10_000);

5.3. @Filter og andre nivå caching

Hvis vi har et høyt belastningsapplikasjon, vil vi definitivt aktivere dvalemodus på andre nivå i dvalemodus, noe som kan være en enorm ytelsesfordel. Vi bør huske på det de @Filter kommentar spiller ikke pent med caching.

Andre nivå cache beholder bare hele ufiltrerte samlinger. Hvis det ikke var tilfelle, kunne vi lese en samling i en økt med filter aktivert, og deretter få den samme bufrede filtrerte samlingen i en annen økt, selv med filter deaktivert.

Dette er grunnen til at @Filter kommentar deaktiverer i utgangspunktet caching for enheten.

6. Kartlegge en hvilken som helst enhetsreferanse med @Noen

Noen ganger vil vi kartlegge en referanse til noen av flere enhetstyper, selv om de ikke er basert på en enkelt @MappedSuperclass. De kan til og med bli kartlagt til forskjellige ikke-relaterte tabeller. Vi kan oppnå dette med @Noen kommentar.

I vårt eksempel, vi må legge ved en beskrivelse til hver enhet i vår utholdenhetsenhet, nemlig, Ansatt og Telefon. Det ville være urimelig å arve alle enheter fra en enkelt abstrakt superklasse bare for å gjøre dette.

6.1. Kartlegging av forhold med @Noen

Slik kan vi definere en referanse til enhver enhet som implementeres Serialiserbar (dvs. til enhver enhet i det hele tatt):

@Entity offentlig klasse EntityDescription implementerer Serialiserbar {privat strengbeskrivelse; @Any (metaDef = "EntityDescriptionMetaDef", metaColumn = @Column (name = "entity_type")) @JoinColumn (name = "entity_id") privat Serialiserbar enhet; }

De metaDef eiendom er navnet på definisjonen, og metakolonne er navnet på kolonnen som skal brukes til å skille enhetstypen (ikke ulik diskriminatorkolonnen i kartleggingen for enkelttabellhierarki).

Vi spesifiserer også kolonnen som vil referere til id av enheten. Det er verdt å merke seg det denne kolonnen vil ikke være en fremmed nøkkel fordi den kan referere til hvilken som helst tabell vi ønsker.

De enhet_id kolonnen kan også generelt ikke være unik fordi forskjellige tabeller kan ha gjentatte identifikatorer.

De enhetstype/enhet_id par skal imidlertid være unikt, ettersom det unikt beskriver enheten vi refererer til.

6.2. Definere @Noen Kartlegging med @AnyMetaDef

Akkurat nå vet ikke dvalemodus hvordan man skal skille mellom forskjellige enhetstyper, fordi vi ikke spesifiserte hva enhetstype kolonne kunne inneholde.

For å få dette til å fungere, må vi legge til metadefinisjonen av kartleggingen med @AnyMetaDef kommentar. Det beste stedet å si det ville være pakkenivået, slik at vi kunne bruke det på andre kartlegginger.

Slik gjør du pakkeinfo.java fil med @AnyMetaDef merknader vil se ut som:

@AnyMetaDef (name = "EntityDescriptionMetaDef", metaType = "string", idType = "int", metaValues ​​= {@MetaValue (value = "Employee", targetEntity = Employee.class), @MetaValue (value = "Phone", targetEntity = Telefon.klasse)}) pakke com.baeldung.hibernate.pojo;

Her har vi spesifisert typen enhetstype kolonne (streng), typen av enhet_id kolonne (int), akseptable verdier i enhetstype kolonne ("Ansatt" og “Telefon”) og tilsvarende enhetstyper.

Anta at vi har en ansatt med to telefoner beskrevet slik:

Ansatt ansatt = ny ansatt (); Telefontelefon1 = ny telefon ("555-45-67"); Telefontelefon2 = ny telefon ("555-89-01"); ansatte.getPhones (). legg til (telefon1); ansatt.getPhones (). legg til (telefon2);

Nå kan vi legge til beskrivende metadata til alle tre enhetene, selv om de har forskjellige ikke-relaterte typer:

EntityDescription employeeDescription = new EntityDescription ("Send til konferanse neste år", ansatt); EntityDescription phone1Description = ny EntityDescription ("Hjemmetelefon (ikke ring etter 22:00)", telefon1); EntityDescription phone2Description = ny EntityDescription ("Arbeidstelefon", telefon1);

7. Konklusjon

I denne artikkelen har vi utforsket noen av Hibernates merknader som tillater finjustering av enhetskartlegging ved hjelp av rå SQL.

Kildekoden for artikkelen er tilgjengelig på GitHub.


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