Dinamično mapiranje s hibernacijom

1. Uvod

U ovom ćemo članku istražiti neke mogućnosti dinamičnog mapiranja hibernacije s @Formula, @Gdje, @Filtar i @ Bilo koji bilješke.

Imajte na umu da iako Hibernate provodi JPA specifikaciju, ovdje opisane bilješke dostupne su samo u Hibernateu i nisu izravno prenosive na druge JPA implementacije.

2. Postavljanje projekta

Da bismo demonstrirali značajke, trebat će nam samo hibernate-core knjižnica i prateća H2 baza podataka:

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

Za trenutnu verziju hibernacija-jezgra knjižnica, krenite prema Maven Central.

3. Izračunati stupci sa @Formula

Pretpostavimo da želimo izračunati vrijednost polja entiteta na temelju nekih drugih svojstava. Jedan od načina za to bio bi definiranjem izračunatog polja samo za čitanje u našem Java entitetu:

@Entity zaposlenik javne klase implementira serijski {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer id; privatni dugi bruto prihod; privatni int porezInPercents; public long getTaxJavaWay () {return bruto dohodak * taxInPercents / 100; }}

Očiti nedostatak je taj morali bismo izvršiti preračun svaki put kad pristupimo ovom virtualnom polju od strane getera.

Bilo bi puno lakše dobiti već izračunatu vrijednost iz baze podataka. To se može učiniti s @Formula napomena:

@Entity zaposlenik javne klase implementira serijski {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer id; privatni dugi bruto prihod; privatni int porezInPercents; @Formula ("bruto prihod * porezInPercenti / 100") privatni dugi porez; }

S @Formula, možemo koristiti podupite, pozivati ​​funkcije izvorne baze podataka i pohranjene procedure i u osnovi raditi sve što ne narušava sintaksu klauzule SQL odabira za ovo polje.

Hibernate je dovoljno pametan da raščlani SQL koji smo dali i umetne ispravne pseudonime tablice i polja. Upozorenje koje morate biti svjesni jest da, budući da je vrijednost napomene sirovi SQL, može ovisiti o našoj bazi podataka mapiranja.

Također, imajte na umu da vrijednost se izračunava kada se entitet preuzima iz baze podataka. Stoga, kada nastavimo ili ažuriramo entitet, vrijednost se neće ponovno izračunati dok entitet ne bude izbačen iz konteksta i ponovno učitan:

Zaposlenik zaposlenik = novi zaposlenik (10_000L, 25); session.save (zaposlenik); session.flush (); session.clear (); zaposlenik = session.get (Employee.class, worker.getId ()); assertThat (worker.getTax ()). isEqualTo (2_500L);

4. Filtriranje entiteta sa @Gdje

Pretpostavimo da želimo pružiti dodatni uvjet za upit kad god zatražimo neki entitet.

Na primjer, moramo implementirati "meko brisanje". To znači da se entitet nikada ne briše iz baze podataka, već se samo označava kao izbrisan s boolean polje.

Morali bismo biti vrlo oprezni sa svim postojećim i budućim upitima u aplikaciji. Morali bismo pružiti ovaj dodatni uvjet svakom upitu. Srećom, Hibernate nudi način da se to učini na jednom mjestu:

@Entity @Where (clause = "delete = false") javna klasa zaposlenik implementira serializable {// ...}

The @Gdje napomena o metodi sadrži SQL klauzulu koja će se dodati bilo kojem upitu ili podupitu ovog entiteta:

zaposlenik.setDeleted (true); session.flush (); session.clear (); zaposlenik = session.find (Employee.class, worker.getId ()); assertThat (zaposlenik) .isNull ();

Kao i u slučaju @Formula napomena, budući da imamo posla sa sirovim SQLom, @Gdje stanje se neće ponovno procijeniti sve dok entitet ne ispraznimo u bazu podataka i ne izbacimo je iz konteksta.

Do tog vremena, entitet će ostati u kontekstu i bit će mu dostupan putem upita i pretraživanja iskaznica.

The @Gdje napomena se također može koristiti za polje zbirke. Pretpostavimo da imamo popis telefona koji se mogu brisati:

@Entity telefon javne klase implementira Serializable {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer id; privatna logička vrijednost izbrisana; privatni broj niza; }

Zatim, iz Zaposlenik sa strane, mogli bismo mapirati kolekciju za brisanje telefoni kako slijedi:

javna klasa Zaposlenik implementira Serializable {// ... @OneToMany @JoinColumn (name = "worker_id") @Where (clause = "delete = false") private Set phones = new HashSet (0); }

Razlika je u tome što Zaposlenik.telefoni zbirka bi se uvijek filtrirala, ali sve telefone, uključujući izbrisane, i dalje bismo mogli dobiti izravnim upitom:

zaposlenik.getPhones (). iterator (). next (). setDeleted (true); session.flush (); session.clear (); zaposlenik = session.find (Employee.class, worker.getId ()); assertThat (worker.getPhones ()). hasSize (1); Popis fullPhoneList = session.createQuery ("s telefona"). GetResultList (); assertThat (fullPhoneList) .hasSize (2);

5. Parametrirano filtriranje sa @Filtar

Problem s @Gdje Napomena je da nam omogućuje da odredimo samo statički upit bez parametara i ne može ga onemogućiti ili omogućiti zahtjev.

The @Filtar napomena djeluje na isti način kao i @Gdje, ali također se može omogućiti ili onemogućiti na razini sesije, a također i parametrizirati.

5.1. Definiranje @Filtar

Da pokažem kako @Filtar radi, dodajmo prvo sljedeću definiciju filtra u Zaposlenik entitet:

@FilterDef (name = "prihodLevelFilter", parametri = @ParamDef (name = "prihodLimit", type = "int")) @Filter (name = "prihodLevelFilter", condition = "bruto prihod>: prihodLimit") javna klasa Zaposlenik implementira serializable {

The @FilterDef napomena definira naziv filtra i skup njegovih parametara koji će sudjelovati u upitu. Tip parametra je naziv jedne od hibernacijskih vrsta (Type, UserType ili CompositeUserType), u našem slučaju, int.

@FilterDef napomena može biti postavljena ili na vrsti ili na razini paketa. Imajte na umu da on ne navodi sam uvjet filtra (iako bismo mogli odrediti defaultCondition parametar).

To znači da filtar (njegovo ime i skup parametara) možemo definirati na jednom mjestu, a zatim drugačije definirati uvjete za filtar na više drugih mjesta.

To se može učiniti s @Filtar bilješka. U našem slučaju, zbog jednostavnosti ga stavljamo u istu klasu. Sintaksa uvjeta je sirovi SQL s imenima parametara ispred kojih su dvotačke.

5.2. Pristup filtriranim entitetima

Još jedna razlika od @Filtar iz @Gdje je li to @Filtar nije omogućeno prema zadanim postavkama. Moramo ga omogućiti na razini sesije ručno i navesti vrijednosti parametara za njega:

session.enableFilter ("prihodLevelFilter") .setParameter ("prihodLimit", 11_000);

Sada pretpostavimo da u bazi imamo sljedeća tri zaposlenika:

session.save (novi zaposlenik (10_000, 25)); session.save (novi zaposlenik (12_000, 25)); session.save (novi zaposlenik (15_000, 25));

Tada će s omogućenim filtrom, kao što je prikazano gore, samo dva od njih biti vidljiva upitom:

Popis zaposlenika = session.createQuery ("od zaposlenika") .getResultList (); assertThat (zaposlenici) .hasSize (2);

Imajte na umu da se omogućeni filtar i vrijednosti parametara primjenjuju samo unutar trenutne sesije. U novoj sesiji bez omogućenog filtra vidjet ćemo sve tri zaposlenice:

session = HibernateUtil.getSessionFactory (). openSession (); zaposlenici = session.createQuery ("od zaposlenika"). getResultList (); assertThat (zaposlenici) .hasSize (3);

Također, kada se izravno dohvaća entitet pomoću id-a, filtar se ne primjenjuje:

Zaposlenik zaposlenik = session.get (Employee.class, 1); assertThat (worker.getGrossIncome ()). isEqualTo (10_000);

5.3. @Filtar i keširanje druge razine

Ako imamo aplikaciju s velikim opterećenjem, svakako bismo htjeli omogućiti Hibernate predmemoriju druge razine, što može biti velika prednost u performansama. To bismo trebali imati na umu the @Filtar anotacija se ne igra lijepo s predmemoriranjem.

Predmemorija druge razine čuva samo pune nefiltrirane zbirke. Ako to nije bio slučaj, mogli bismo čitati zbirku u jednoj sesiji s omogućenim filtrom, a zatim dobiti istu predmemoriranu filtriranu zbirku u drugoj sesiji, čak i kad je filtar onemogućen.

To je razlog zašto @Filtar napomena u osnovi onemogućuje predmemoriranje entiteta.

6. Mapiranje bilo koje reference entiteta sa @ Bilo koji

Ponekad želimo preslikati referencu na bilo koji od više vrsta entiteta, čak i ako se ne temelje na jednom @MappedSuperclass. Čak bi se mogli preslikati u različite nepovezane tablice. To možemo postići pomoću @ Bilo koji bilješka.

U našem primjeru, morat ćemo priložiti opis svakom entitetu u našoj jedinici postojanosti, naime, Zaposlenik i Telefon. Bilo bi nerazumno naslijediti sve entitete iz jedne apstraktne superklase samo da bi se to učinilo.

6.1. Mapiranje odnosa sa @ Bilo koji

Evo kako možemo definirati referencu na bilo koji entitet koji implementira Serijalizirati (tj. uopće bilo kojem entitetu):

@Entity javna klasa EntityDescription implementira Serializable {opis privatnog niza; @ Bilo koji (metaDef = "EntityDescriptionMetaDef", metaColumn = @Column (name = "entity_type")) @JoinColumn (name = "entity_id") privatni serializabilni entitet; }

The metaDef svojstvo je naziv definicije, i metakolona je naziv stupca koji će se koristiti za razlikovanje tipa entiteta (za razliku od stupca diskriminator u mapiranju hijerarhije pojedine tablice).

Također odredujemo stupac koji će se pozivati ​​na iskaznica entiteta. Vrijedno je to napomenuti ovaj stupac neće biti strani ključ jer se može pozivati ​​na bilo koju tablicu koju želimo.

The entitet_id stupac također općenito ne može biti jedinstven jer različite tablice mogu imati ponovljene identifikatore.

The vrsta_ entiteta/entitet_id par, međutim, trebao bi biti jedinstven, jer jedinstveno opisuje entitet na koji se pozivamo.

6.2. Definiranje @ Bilo koji Mapiranje sa @AnyMetaDef

Trenutno Hibernate ne zna razlikovati različite tipove entiteta, jer nismo naveli što vrsta_ entiteta stupac mogao sadržavati.

Da bi ovo uspjelo, moramo dodati meta-definiciju mapiranja s @AnyMetaDef bilješka. Najbolje mjesto za postavljanje bila bi razina paketa, pa bismo je mogli ponovno koristiti u drugim preslikavanjima.

Evo kako package-info.java datoteka s @AnyMetaDef napomena bi izgledala ovako:

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

Ovdje smo naveli vrstu vrsta_ entiteta stupac (niz), vrsta entitet_id stupac (int), prihvatljive vrijednosti u vrsta_ entiteta stupac ("Zaposlenik" i "Telefon") i odgovarajuće vrste entiteta.

Pretpostavimo sada da imamo zaposlenika s dva ovako opisana telefona:

Zaposlenik zaposlenik = novi zaposlenik (); Telefonski telefon1 = novi Telefon ("555-45-67"); Telefonski telefon2 = novi Telefon ("555-89-01"); zaposlenik.getPhones (). add (phone1); zaposlenik.getPhones (). add (phone2);

Sada bismo mogli dodati opisne metapodatke za sva tri entiteta, iako imaju različite nepovezane vrste:

EntityDescription workerDescription = novi EntityDescription ("Pošalji konferenciji sljedeće godine", zaposlenik); EntityDescription phone1Description = novi EntityDescription ("Kućni telefon (ne zovi nakon 22 sata)", phone1); EntityDescription phone2Description = novi EntityDescription ("Radni telefon", phone1);

7. Zaključak

U ovom smo članku istražili neke napomene Hibernate-a koje omogućuju fino podešavanje mapiranja entiteta pomoću sirovog SQL-a.

Izvorni kôd članka dostupan je na GitHubu.


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