Dinamikus leképezés hibernált állapotban

1. Bemutatkozás

Ebben a cikkben a Hibernate néhány dinamikus leképezési képességét vizsgáljuk meg a @Képlet, @Hol, @Szűrő és @Bármi annotációk.

Vegye figyelembe, hogy bár a Hibernate végrehajtja a JPA specifikációt, az itt leírt kommentárok csak Hibernate módban érhetők el, és közvetlenül nem hordozhatók más JPA megvalósításokban.

2. Projekt beállítása

A funkciók bemutatásához csak a hibernált magú könyvtárra és egy támogató H2 adatbázisra lesz szükségünk:

 org. hibernálja a hibernált magot 5.4.12. Végső com.h2adatbázis h2 1.4.194 

Az aktuális verzióhoz hibernált mag könyvtár, irány a Maven Central.

3. Számított oszlopok With @Képlet

Tegyük fel, hogy egy entitás mező értékét néhány más tulajdonság alapján szeretnénk kiszámítani. Ennek egyik módja egy kiszámított, csak olvasható mező meghatározása a Java entitásunkban:

@Entity public class Az alkalmazott alkalmazza a Serializálható {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer azonosítót; magán hosszú bruttó jövedelem; magán int adóInPercents; public long getTaxJavaWay () {return bruttó bevétel * taxInPercents / 100; }}

A nyilvánvaló hátrány az minden alkalommal meg kell végeznünk az újraszámítást, amikor a virtuális mezőhöz érünk a getter segítségével.

Sokkal könnyebb lenne megszerezni az adatbázisból a már kiszámolt értéket. Ez a @Képlet kommentár:

@Entity public class Alkalmazott végrehajtja a Serializable {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer azonosítót; magán hosszú bruttó jövedelem; magán int adóInPercents; @Formula ("bruttó jövedelem * taxInPercents / 100") magán hosszú adó; }

Val vel @Képlet, használhatunk alkérdezéseket, meghívhatunk natív adatbázis-függvényeket és tárolt eljárásokat, és alapvetően bármit megtehetünk, ami nem bontja meg az SQL kiválasztási záradék szintaxisát erre a mezőre.

A hibernálás elég okos ahhoz, hogy elemezze az általunk megadott SQL-t, és helyesen illessze be a tábla- és mezőneveket. A figyelmeztetés az, hogy tisztában kell lenni azzal, hogy mivel az annotáció értéke nyers SQL, a térképezési adatbázisunkat függővé teheti.

Ezt is tartsa szem előtt az érték kiszámítása akkor történik, amikor az entitást lekérik az adatbázisból. Ennélfogva, amikor továbbra is fennállunk vagy frissítjük az entitást, az értéket addig nem számolják újra, amíg az entitást ki nem emelik a kontextusból és újra betöltik:

Alkalmazott alkalmazott = új alkalmazott (10_000L, 25); session.save (alkalmazott); session.flush (); session.clear (); alkalmazott = session.get (Employee.class, worker.getId ()); assertThat (alkalmazott.getTax ()). isEqualTo (2_500L);

4. Entitások szűrése a következővel: @Hol

Tegyük fel, hogy további feltételeket akarunk biztosítani a lekérdezéshez, amikor valamilyen entitást kérünk.

Például meg kell valósítanunk a „soft delete” -et. Ez azt jelenti, hogy az entitást soha nem törlik az adatbázisból, hanem csak a-val töröltként jelölik logikai terület.

Nagy gondot kell fordítanunk az alkalmazás összes meglévő és jövőbeli kérdésére. Ezt a kiegészítő feltételt minden lekérdezésnek meg kell adnunk. Szerencsére a Hibernate módot kínál erre egy helyen:

@Entity @Where (clause = "törölve = hamis") nyilvános osztály Az alkalmazottak Serializálhatót hajtanak végre {// ...}

A @Hol A metódus kommentárja tartalmaz egy SQL záradékot, amelyet hozzáadnak az entitás bármely lekérdezéséhez vagy allekérdezéséhez:

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

Mint a @Képlet kommentár, mivel nyers SQL-el van dolgunk, a @Hol A feltétel addig nem kerül átértékelésre, amíg az entitást át nem dobjuk az adatbázisba, és ki nem emeljük a kontextusból.

Addig az entitás a kontextusban marad, és elérhető lesz lekérdezésekkel és keresésekkel id.

A @Hol annotáció használható egy gyűjtőmezőhöz is. Tegyük fel, hogy van egy törölhető telefonok listája:

@Entity public class Phone megvalósítja a Serializable {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer azonosítót; privát logikai érték törölve; privát karakterlánc száma; }

Aztán a Munkavállaló oldalon feltérképezhetnénk egy törölhető gyűjteményt telefonok alábbiak szerint:

public class Alkalmazott Serializálható {// ... @OneToMany @JoinColumn (név = "alkalmazott_azonosító") @Hol (záradék = "törölt = hamis") privát Telefonkészülék = új HashSet (0); }

A különbség az, hogy a Alkalmazott.telefonok a gyűjtemény mindig szűrve lenne, de így is megkaphatnánk az összes telefont, beleértve a törölteket is, közvetlen lekérdezéssel:

alkalmazott.getPhones (). iterator (). next (). setDeleted (true); session.flush (); session.clear (); alkalmazott = session.find (Employee.class, worker.getId ()); assertThat (alkalmazott.getPhones ()). hasSize (1); List fullPhoneList = session.createQuery ("telefonról"). GetResultList (); assertThat (fullPhoneList) .hasSize (2);

5. Paraméteres szűrés @Szűrő

A probléma a @Hol az annotáció az, hogy lehetővé teszi számunkra, hogy csak statikus lekérdezést adjunk meg paraméterek nélkül, és igény szerint nem lehet letiltani vagy engedélyezni.

A @Szűrő az annotáció ugyanúgy működik, mint a @Hol, de a munkamenet szintjén is engedélyezhető vagy letiltható, és paraméterezhető is.

5.1. A @Szűrő

Hogy bemutassam, hogyan @Szűrő működik, először adjuk hozzá a következő szűrő definíciót a Munkavállaló entitás:

@FilterDef (név = "jövedelemszintFilter", paraméterek = @ParamDef (név = "jövedelemLimit", típus = "int")) @Filter (név = "jövedelemszint": feltétel = "bruttó jövedelem>: jövedelemLimit") nyilvános osztály Az alkalmazottak Serializálhatók {

A @FilterDef az annotáció határozza meg a szűrő nevét és a paraméterek halmazát, amelyek részt vesznek a lekérdezésben. A paraméter típusa az egyik hibernált típus neve (Type, UserType vagy CompositeUserType), esetünkben egy int.

A @FilterDef kommentár elhelyezhető akár típus, akár csomag szintjén. Vegye figyelembe, hogy nem adja meg magát a szűrési feltételt (bár megadhatnánk a defaultCondition paraméter).

Ez azt jelenti, hogy egy helyen definiálhatjuk a szűrőt (annak nevét és paraméterkészletét), majd különböző módon meghatározhatjuk a szűrő feltételeit több más helyen is.

Ez a @Szűrő annotáció. Esetünkben az egyszerűség kedvéért ugyanabba az osztályba soroljuk. A feltétel szintaxisa egy nyers SQL, paraméternevekkel kettőspontok előtt.

5.2. Szűrt entitások elérése

Egy másik különbség @Szűrő tól től @Hol az, hogy a @Szűrő alapértelmezés szerint nincs engedélyezve. Manuálisan engedélyeznünk kell a munkamenet szintjén, és meg kell adnunk a paraméterértékeket:

session.enableFilter ("jövedelemszintFilter") .setParameter ("jövedelemLimit", 11_000);

Tegyük fel, hogy a következő három alkalmazott van az adatbázisban:

session.save (új alkalmazott (10_000, 25)); session.save (új alkalmazott (12_000, 25)); session.save (új alkalmazott (15_000, 25));

Ezután, ha a szűrő engedélyezve van, a fentiek szerint, csak kettő lesz látható lekérdezéssel:

Alkalmazottak listája = session.createQuery ("az alkalmazotttól") .getResultList (); assertThat (alkalmazottak) .hasSize (2);

Vegye figyelembe, hogy az engedélyezett szűrő és annak paraméterértékei csak az aktuális munkameneten belül kerülnek alkalmazásra. Új szűrés nélkül engedélyezett munkamenetben mindhárom alkalmazottat látni fogjuk:

session = HibernateUtil.getSessionFactory (). openSession (); alkalmazottak = session.createQuery ("a munkavállalótól"). getResultList (); assertThat (alkalmazottak) .hasSize (3);

Továbbá, ha az entitást közvetlenül beolvassa azonosítóval, akkor a szűrő nem kerül alkalmazásra:

Alkalmazott alkalmazott = session.get (Employee.class, 1); assertThat (alkalmazott.getGrossIncome ()). isEqualTo (10_000);

5.3. @Szűrő és második szintű gyorsítótár

Ha van egy nagy terhelésű alkalmazásunk, akkor feltétlenül szeretnénk engedélyezni a Hibernate második szintű gyorsítótárat, ami hatalmas teljesítményelőnyt jelenthet. Ezt szem előtt kell tartanunk a @Szűrő az annotáció nem játszik jól a gyorsítótárral.

A második szintű gyorsítótár csak teljes szűretlen gyűjteményeket tárol. Ha nem ez volt a helyzet, akkor egy munkamenetben olvashattunk egy gyűjteményt engedélyezett szűrővel, majd ugyanazt a gyorsítótárazott szűrt gyűjteményt kaptuk egy másik munkamenetben, még akkor is, ha a szűrő le volt tiltva.

Ezért a @Szűrő az annotáció alapvetően letiltja az entitás gyorsítótárát.

6. Minden entitás hivatkozásának leképezése a @Bármi

Néha hivatkozást akarunk feltérképezni bármely entitástípus bármelyikére, még akkor is, ha azok nem egyetlen elemen alapulnak @MappedSuperclass. Akár különböző, nem kapcsolódó táblázatokhoz is leképezhetők. Ezt elérhetjük a @Bármi annotáció.

Példánkban mellékelni kell néhány leírást a perzisztencia egységünk minden eleméheznevezetesen Munkavállaló és Telefon. Indokolatlan lenne minden entitást egyetlen absztrakt szuperosztályból örökölni, csak ennek érdekében.

6.1. Kapcsolat feltérképezése @Bármi

Így definiálhatunk hivatkozást minden entitásra, amely végrehajtja Sorosítható (vagyis egyáltalán bármely entitáshoz):

@Entity public class Az EntityDescription a Serializable {private String description; @Any (metaDef = "EntityDescriptionMetaDef", metaColumn = @Column (név = "entitás_típus")) @JoinColumn (név = "entitásazonosító") privát Serializálható entitás; }

A metaDef tulajdonság a definíció neve, és metaColumn annak az oszlopnak a neve, amelyet az entitás típusának megkülönböztetésére használnak (nem ellentétben az egyetlen táblázat hierarchiájának leképezésében szereplő megkülönböztető oszloppal).

Megadjuk azt az oszlopot is, amelyre hivatkozni fog id az entitás. Érdemes ezt megjegyezni ez az oszlop nem lesz idegen kulcs mert hivatkozhat bármelyik táblára, amelyet akarunk.

A entitásazonosító oszlop általában nem lehet egyedi, mert a különböző tábláknak ismétlődő azonosítójuk lehet.

A entitás típusa/entitásazonosító A párnak azonban egyedinek kell lennie, mivel egyedileg írja le azt az entitást, amelyre hivatkozunk.

6.2. A @Bármi Térképezés vele @AnyMetaDef

Jelenleg a Hibernate nem tudja, hogyan lehet megkülönböztetni a különböző entitástípusokat, mert nem adtuk meg, hogy mi az entitás típusa oszlop tartalmazhat.

Ahhoz, hogy ez működjön, hozzá kell adnunk a leképezés metadefinícióját a @AnyMetaDef annotáció. A legjobb hely a csomag szintjén lenne, így más leképezésekben újra felhasználhatnánk.

Itt van, hogyan package-info.java fájl a @AnyMetaDef a kommentár a következőképpen nézne ki:

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

Itt adtuk meg a típus típusát entitás típusa oszlop (húr), a entitásazonosító oszlop (int), az elfogadható értékeket a entitás típusa oszlop ("Munkavállaló" és "Telefon") és a megfelelő entitástípusokat.

Tegyük fel, hogy van egy alkalmazottunk, akinek két ilyen telefonja van:

Alkalmazott alkalmazott = új alkalmazott (); Telefon telefon1 = új Telefon ("555-45-67"); Telefon telefon2 = új Telefon ("555-89-01"); alkalmazott.getPhones (). add (telefon1); alkalmazott.getPhones (). add (telefon2);

Most hozzáadhatunk leíró metaadatokat mindhárom entitáshoz, annak ellenére, hogy különböző, nem kapcsolódó típusaik vannak:

EntityDescription workerDescription = új EntityDescription ("Küldés konferenciára jövőre", alkalmazott); EntityDescription phone1Description = új EntityDescription ("Otthoni telefon (22:00 után ne hívjon)", phone1); EntityDescription phone2Description = új EntityDescription ("Munkahelyi telefon", telefon1);

7. Következtetés

Ebben a cikkben a Hibernate néhány feljegyzését vizsgáltuk, amelyek lehetővé teszik az entitások leképezésének finomhangolását a nyers SQL használatával.

A cikk forráskódja elérhető a GitHubon.


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