Dynamisk kortlægning med dvale

1. Introduktion

I denne artikel undersøger vi nogle dynamiske kortlægningsfunktioner i dvale med @Formel, @Hvor, @Filter og @Nogen kommentarer.

Bemærk, at selvom dvaletilstand implementerer JPA-specifikationen, er kommentarerne beskrevet her kun tilgængelige i dvaletilstand og er ikke direkte bærbare til andre JPA-implementeringer.

2. Opsætning af projekt

For at demonstrere funktionerne har vi kun brug for dvale-core-biblioteket og en understøttende H2-database:

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

For den aktuelle version af dvale-kerne bibliotek, gå over til Maven Central.

3. Beregnede kolonner med @Formel

Antag, at vi vil beregne en enhedsfeltværdi baseret på nogle andre egenskaber. En måde at gøre det på ville være ved at definere et beregnet skrivebeskyttet felt i vores Java-enhed:

@Entity offentlig klasse Medarbejder implementerer Serialiserbar {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat heltal id; privat lang bruttoindkomst; privat int skat InPercents; offentlig lang getTaxJavaWay () {return grossIncome * taxInPercents / 100; }}

Den åbenlyse ulempe er, at vi bliver nødt til at foretage genberegningen hver gang vi får adgang til dette virtuelle felt af getter.

Det ville være meget lettere at hente den allerede beregnede værdi fra databasen. Dette kan gøres med @Formel kommentar:

@Entity offentlig klasse Medarbejder implementerer Serialiserbar {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat heltal id; privat lang bruttoindkomst; privat int skatInPercents; @Formula ("grossIncome * taxInPercents / 100") privat lang skat; }

Med @Formel, kan vi bruge underforespørgsler, kalde native databasefunktioner og lagrede procedurer og grundlæggende gøre alt, hvad der ikke bryder syntaksen for en SQL-valgklausul til dette felt.

Dvaletilstand er smart nok til at analysere den SQL, vi leverede, og indsætte korrekte tabel- og feltaliaser. Advarslen at være opmærksom på er, at da værdien af ​​kommentaren er rå SQL, kan den muligvis gøre vores kortlægningsdatabaseafhængig.

Husk også det værdien beregnes, når enheden hentes fra databasen. Derfor, når vi vedvarer eller opdaterer enheden, beregnes værdien ikke igen, før enheden bortkastes fra konteksten og indlæses igen:

Medarbejdermedarbejder = ny medarbejder (10_000L, 25); session.save (medarbejder); session.flush (); session.clear (); medarbejder = session.get (Medarbejder.klasse, medarbejder.getId ()); assertThat (medarbejder.getTaks ()). erEqualTo (2_500L);

4. Filtrering af enheder med @Hvor

Antag, at vi ønsker at give en yderligere betingelse til forespørgslen, når vi anmoder om en enhed.

For eksempel er vi nødt til at implementere "soft delete". Dette betyder, at enheden aldrig slettes fra databasen, men kun markeres som slettet med en boolsk Mark.

Vi bliver nødt til at være meget forsigtige med alle eksisterende og fremtidige forespørgsler i applikationen. Vi bliver nødt til at give denne yderligere betingelse til hver forespørgsel. Heldigvis giver dvale en måde at gøre dette på ét sted:

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

Det @Hvor kommentar på en metode indeholder en SQL-klausul, der føjes til enhver forespørgsel eller underforespørgsel til denne enhed:

medarbejder.setDeleted (sand); session.flush (); session.clear (); medarbejder = session.find (Medarbejder.klasse, medarbejder.getId ()); assertThat (medarbejder) .isNull ();

Som i tilfældet med @Formel kommentar, da vi har at gøre med rå SQL, er @Hvor betingelse vil ikke blive revurderet, før vi skubber enheden til databasen og kaster den ud af konteksten.

Indtil det tidspunkt forbliver enheden i konteksten og vil være tilgængelig med forespørgsler og opslag af id.

Det @Hvor annotering kan også bruges til et indsamlingsfelt. Antag, at vi har en liste over slettelige telefoner:

@Entity offentlig klasse Telefonimplementer Serialiserbar {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat heltal id; privat boolsk slettet; privat streng nummer; }

Derefter fra Medarbejder side kunne vi kortlægge en samling sletbare telefoner som følger:

offentlig klasse Medarbejderimplementer Serialiserbar {// ... @OneToMany @JoinColumn (navn = "medarbejder_id") @Where (paragraf = "slettet = falsk") privat Sæt telefoner = nyt HashSet (0); }

Forskellen er, at Medarbejdertelefoner samling ville altid blive filtreret, men vi kunne stadig få alle telefoner, inklusive slettede, via direkte forespørgsel:

medarbejder.getPhones (). iterator (). næste (). setDeleted (sand); session.flush (); session.clear (); medarbejder = session.find (Medarbejder.klasse, medarbejder.getId ()); assertThat (medarbejder.getPhones ()). har størrelse (1); Liste over FullPhoneList = session.createQuery ("fra telefon"). GetResultList (); assertThat (fullPhoneList) .hasSize (2);

5. Parametreret filtrering med @Filter

Problemet med @Hvor kommentar er, at det kun giver os mulighed for at angive en statisk forespørgsel uden parametre, og den kan ikke deaktiveres eller aktiveres af efterspørgsel.

Det @Filter kommentar fungerer på samme måde som @Hvor, men det kan også aktiveres eller deaktiveres på sessionsniveau og også parametriseres.

5.1. Definition af @Filter

At demonstrere hvordan @Filter fungerer, lad os først tilføje følgende filterdefinition til Medarbejder enhed:

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

Det @FilterDef annotation definerer filternavnet og et sæt af dets parametre, der vil deltage i forespørgslen. Parameterens type er navnet på en af ​​dvale-typerne (Type, UserType eller CompositeUserType), i vores tilfælde en int.

@FilterDef kommentar kan placeres enten på typen eller på pakkeniveau. Bemærk, at det ikke angiver selve filtertilstanden (selvom vi kunne specificere defaultCondition parameter).

Dette betyder, at vi kan definere filteret (dets navn og sæt af parametre) ét sted og derefter definere betingelserne for filteret flere forskellige steder forskelligt.

Dette kan gøres med @Filter kommentar. I vores tilfælde lægger vi det i samme klasse for enkelhedens skyld. Syntaksen for betingelsen er en rå SQL med parameternavne forud for kolon.

5.2. Adgang til filtrerede enheder

En anden forskel på @Filter fra @Hvor er det @Filter er ikke aktiveret som standard. Vi er nødt til at aktivere det manuelt på sessionsniveau og give parameterværdierne til det:

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

Antag nu, at vi har følgende tre medarbejdere i databasen:

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

Så når filteret er aktiveret, som vist ovenfor, vil kun to af dem være synlige ved forespørgsel:

Liste medarbejdere = session.createQuery ("fra medarbejder") .getResultList (); hævder, at (medarbejdere) .hasSize (2);

Bemærk, at både det aktiverede filter og dets parameterværdier kun anvendes inden for den aktuelle session. I en ny session uden filter aktiveret ser vi alle tre medarbejdere:

session = HibernateUtil.getSessionFactory (). openSession (); medarbejdere = session.createQuery ("fra medarbejder"). getResultList (); hævder, at (medarbejdere) .har størrelse (3);

Når filteret direkte hentes efter id, anvendes filteret heller ikke:

Medarbejdermedarbejder = session.get (Medarbejderklasse, 1); assertThat (medarbejder.getGrossinkomst ()). er lig med (10_000);

5.3. @Filter og cache-niveau på andet niveau

Hvis vi har en applikation med høj belastning, vil vi bestemt aktivere dvale på andet niveau cache, hvilket kan være en enorm ydelsesfordel. Det skal vi huske på det @Filter kommentar spiller ikke pænt med caching.

Anden niveau cache opbevarer kun fulde ufiltrerede samlinger. Hvis det ikke var tilfældet, kunne vi læse en samling i en session med filter aktiveret og derefter få den samme cachelagrede filtrerede samling i en anden session selv med filter deaktiveret.

Dette er grunden til, at @Filter annotering deaktiverer grundlæggende caching for enheden.

6. Kortlægning af enhver enhedsreference med @Nogen

Nogle gange vil vi kortlægge en henvisning til en hvilken som helst af flere enhedstyper, selvom de ikke er baseret på en enkelt @MappedSuperclass. De kunne endda kortlægges til forskellige ikke-relaterede tabeller. Vi kan opnå dette med @Nogen kommentar.

I vores eksempel vi bliver nødt til at vedhæfte en beskrivelse til hver enhed i vores vedholdenhedsenhed, nemlig Medarbejder og telefon. Det ville være urimeligt at arve alle enheder fra en enkelt abstrakt superklasse bare for at gøre dette.

6.1. Kortlægning af relation med @Nogen

Her er hvordan vi kan definere en henvisning til enhver enhed, der implementeres Serialiserbar (dvs. overhovedet til enhver enhed):

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

Det metaDef egenskab er navnet på definitionen, og metakolonne er navnet på den kolonne, der vil blive brugt til at skelne enhedstypen (ikke i modsætning til diskriminatorkolonnen i kortlægningen af ​​den enkelte tabelhierarki).

Vi specificerer også den kolonne, der skal henvise til id af enheden. Det er værd at bemærke det denne kolonne vil ikke være en fremmed nøgle fordi det kan henvise til enhver tabel, vi ønsker.

Det enhed_id kolonne kan generelt ikke være unik, fordi forskellige tabeller kan have gentagne identifikatorer.

Det enhedstype/enhed_id par skal dog være unikt, da det entydigt beskriver den enhed, som vi henviser til.

6.2. Definition af @Nogen Kortlægning med @AnyMetaDef

Lige nu ved dvale ikke, hvordan man skelner mellem forskellige enhedstyper, fordi vi ikke specificerede, hvad enhedstype kolonne kunne indeholde.

For at få dette til at fungere skal vi tilføje metadefinitionen af ​​kortlægningen med @AnyMetaDef kommentar. Det bedste sted at sætte det ville være pakkeniveauet, så vi kunne genbruge det i andre kortlægninger.

Sådan gør du pakke-info.java fil med @AnyMetaDef annotering ser ud 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 specificeret typen af enhedstype kolonne (snor), typen af enhed_id kolonne (int), de acceptable værdier i enhedstype kolonne (“Medarbejder” og "Telefon") og de tilsvarende enhedstyper.

Antag nu, at vi har en medarbejder med to telefoner, der er beskrevet således:

Medarbejdermedarbejder = ny medarbejder (); Telefontelefon1 = ny telefon ("555-45-67"); Telefon telefon2 = ny telefon ("555-89-01"); medarbejder.getPhones (). tilføj (telefon1); medarbejder.getPhones (). tilføj (telefon2);

Nu kunne vi tilføje beskrivende metadata til alle tre enheder, selvom de har forskellige ikke-relaterede typer:

EntityDescription employeeDescription = ny EntityDescription ("Send til konference næste år", medarbejder); EntityDescription phone1Description = ny EntityDescription ("Hjemmetelefon (ring ikke efter 22:00)", telefon1); EntityDescription phone2Description = ny EntityDescription ("Arbejdstelefon", telefon1);

7. Konklusion

I denne artikel har vi undersøgt nogle af Hibernates annoteringer, der muliggør finjustering af enhedskortlægning ved hjælp af rå SQL.

Kildekoden til artiklen er tilgængelig på GitHub.


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