Dynamické mapovanie s režimom dlhodobého spánku

1. Úvod

V tomto článku preskúmame niektoré možnosti dynamického mapovania režimu dlhodobého spánku s @Formula, @Kde, @Filter a @Akýkoľvek anotácie.

Všimnite si, že hoci Hibernate implementuje špecifikáciu JPA, anotácie tu opísané sú k dispozícii iba v Hibernate a nie sú priamo prenosné na iné implementácie JPA.

2. Nastavenie projektu

Na demonštráciu funkcií budeme potrebovať iba knižnicu hibernácie a podpornú databázu H2:

 org.hibernate hibernate-core 5.4.12. Konečná kom.h2databáza h2 1.4.194 

Pre aktuálnu verziu hibernácia-jadro knižnica, choďte do Maven Central.

3. Vypočítané stĺpce s @Formula

Predpokladajme, že chceme vypočítať hodnotu poľa entity na základe niektorých ďalších vlastností. Jedným zo spôsobov, ako to urobiť, by bolo definovanie vypočítaného poľa iba na čítanie v našej entite Java:

@Entity public class Zamestnanec implementuje Serializable {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) súkromné ​​celé číslo; súkromný dlhý hrubý príjem; private int taxInPercents; public long getTaxJavaWay () {return GrossIncome * taxInPercents / 100; }}

Zjavnou nevýhodou je to museli by sme urobiť prepočet zakaždým, keď getter vstúpime do tohto virtuálneho poľa.

Bolo by oveľa jednoduchšie získať už vypočítanú hodnotu z databázy. To sa dá urobiť pomocou @Formula anotácia:

@Entity public class Zamestnanec implementuje Serializable {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) súkromné ​​celé číslo; súkromný dlhý hrubý príjem; private int taxInPercents; @Formula („brutto príjem * taxInPercents / 100“) dlhá súkromná; }

S @Formula, môžeme používať poddotazy, volať funkcie natívnej databázy a uložené procedúry a v podstate robiť čokoľvek, čo neporuší syntax klauzuly SQL select pre toto pole.

Hibernácia je dosť inteligentná na to, aby analyzovala poskytnuté SQL a vložila správne aliasy tabuliek a polí. Výhradou, ktorú je potrebné si uvedomiť, je, že keďže hodnota anotácie je surový SQL, môže to závisieť od našej mapovacej databázy.

Pamätajte tiež na to hodnota sa počíta pri načítaní entity z databázy. Preto keď perzistujeme alebo aktualizujeme entitu, hodnota sa neprepočíta, kým nebude entita vykázaná z kontextu a znovu načítaná:

Zamestnanec zamestnanec = nový zamestnanec (10_000L, 25); session.save (zamestnanec); session.flush (); session.clear (); zamestnanec = session.get (Employee.class, employee.getId ()); assertThat (employee.getTax ()). isEqualTo (2_500L);

4. Filtrovanie entít pomocou @Kde

Predpokladajme, že chceme poskytnúť dotazu ďalšiu podmienku, kedykoľvek požadujeme nejakú entitu.

Napríklad musíme implementovať „mäkké odstránenie“. To znamená, že entita sa nikdy neodstráni z databázy, ale iba sa označí ako odstránená pomocou boolovský lúka.

Museli by sme venovať veľkú pozornosť všetkým existujúcim aj budúcim dotazom v aplikácii. Túto ďalšiu podmienku by sme museli poskytnúť každému dotazu. Našťastie, Hibernate poskytuje spôsob, ako to urobiť na jednom mieste:

@Entity @Where (clause = "deleted = false") verejná trieda Zamestnanec implementuje Serializable {// ...}

The @Kde anotácia metódy obsahuje klauzulu SQL, ktorá sa pridá do každého dotazu alebo poddotazu na túto entitu:

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

Ako v prípade @Formula anotácia, keďže máme do činenia so surovým SQL, @Kde podmienka nebude prehodnotená, kým nevypláchneme entitu do databázy a nevylúčime ju z kontextu.

Do tej doby zostane entita v kontexte a bude prístupná pomocou dotazov a vyhľadávaní používateľom id.

The @Kde anotáciu možno použiť aj pre zberové pole. Predpokladajme, že máme zoznam vymazateľných telefónov:

@Entity verejná trieda Telefón implementuje Serializable {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) súkromné ​​celé číslo; súkromný booleov vymazaný; súkromné ​​číslo reťazca; }

Potom z Zamestnanec strane by sme mohli zmapovať zbierku vymazateľných telefóny nasledovne:

verejná trieda Zamestnanec implementuje Serializable {// ... @OneToMany @JoinColumn (name = "employee_id") @Where (clause = "deleted = false") private Nastaviť telefóny = nová HashSet (0); }

Rozdiel je v tom, že Zamestnanec. Telefóny zbierka by bola vždy filtrovaná, ale stále by sme mohli získať všetky telefóny vrátane odstránených pomocou priameho dotazu:

employee.getPhones (). iterator (). next (). setDeleted (true); session.flush (); session.clear (); zamestnanec = session.find (Employee.class, employee.getId ()); assertThat (employee.getPhones ()). hasSize (1); Zoznam fullPhoneList = session.createQuery ("z telefónu"). GetResultList (); assertThat (fullPhoneList) .hasSize (2);

5. Parametrizované filtrovanie pomocou @Filter

Problém s @Kde anotácia je, že nám umožňuje určiť iba statický dotaz bez parametrov a nemožno ho zakázať alebo povoliť na základe dopytu.

The @Filter anotácia funguje rovnako ako @Kde, ale dá sa tiež povoliť alebo zakázať na úrovni relácie a tiež parametrizovať.

5.1. Definovanie @Filter

Aby sme demonštrovali ako @Filter funguje, najskôr do definície filtra pridajme nasledujúcu definíciu filtra Zamestnanec subjekt:

@FilterDef (name = "incomeLevelFilter", parametre = @ParamDef (name = "incomeLimit", type = "int")) @Filter (name = "incomeLevelFilter", podmienka = "grossIncome>: incomeLimit") verejná trieda Zamestnanec implementuje Serializable {

The @FilterDef anotácia definuje názov filtra a množinu jeho parametrov, ktoré sa zúčastnia dotazu. Typ parametra je názov jedného z typov dlhodobého spánku (Type, UserType alebo CompositeUserType), v našom prípade int.

@FilterDef anotácia môže byť umiestnená buď na type, alebo na úrovni balíka. Upozorňujeme, že neurčuje samotnú podmienku filtra (aj keď by sme mohli určiť defaultCondition parameter).

To znamená, že môžeme definovať filter (jeho názov a množinu parametrov) na jednom mieste a potom definovať podmienky pre filter na viacerých ďalších miestach odlišne.

To sa dá urobiť pomocou @Filter anotácia. V našom prípade to pre jednoduchosť zaraďujeme do rovnakej triedy. Syntax podmienky je surový SQL s názvami parametrov, pred ktorými sú dvojbodky.

5.2. Prístup k filtrovaným entitám

Ďalší rozdiel @Filter od @Kde je to tak @Filter nie je predvolene povolený. Musíme to povoliť manuálne na úrovni relácie a poskytnúť k tomu hodnoty parametrov:

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

Teraz predpokladajme, že máme v databáze týchto troch zamestnancov:

session.save (nový zamestnanec (10_000, 25)); session.save (nový zamestnanec (12_000, 25)); session.save (nový zamestnanec (15_000, 25));

Potom so zapnutým filtrom, ako je uvedené vyššie, budú dotazom viditeľné iba dva z nich:

Zoznam zamestnancov = session.createQuery ("od zamestnanca") .getResultList (); assertThat (zamestnanci) .hasSize (2);

Upozorňujeme, že povolený filter aj jeho hodnoty parametrov sa používajú iba v rámci aktuálnej relácie. V novej relácii bez povoleného filtra uvidíme všetkých troch zamestnancov:

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

Pri priamom načítaní entity podľa id sa filter nepoužije:

Zamestnanec zamestnanec = session.get (Employee.class, 1); assertThat (employee.getGrossIncome ()). isEqualTo (10_000);

5.3. @Filter a medzipamäť druhej úrovne

Ak máme aplikáciu s vysokým zaťažením, určite by sme chceli povoliť medzipamäť Hibernate druhej úrovne, čo môže byť obrovskou výhodou výkonu. Mali by sme na to pamätať the @Filter anotácia sa s cachovaním nehrá pekne.

Vyrovnávacia pamäť druhej úrovne uchováva iba úplné nefiltrované zbierky. Keby to tak nebolo, mohli by sme si prečítať kolekciu v jednej relácii so zapnutým filtrom a potom získať tú istú filtrovanú kolekciu v pamäti v inej relácii aj s vypnutým filtrom.

To je dôvod, prečo @Filter anotácia v podstate zakazuje ukladanie do vyrovnávacej pamäte pre entitu.

6. Mapovanie akejkoľvek referencie entity na @Akýkoľvek

Niekedy chceme mapovať odkaz na ktorýkoľvek z viacerých typov entít, aj keď nie sú založené na jednom @MappedSuperclass. Mohli by byť dokonca mapované do rôznych nesúvisiacich tabuliek. To môžeme dosiahnuť pomocou @Akýkoľvek anotácia.

V našom príklade budeme musieť pripojiť nejaký popis ku každej entite v našej jednotke perzistencie, a to Zamestnanec a Telefón. Bolo by nerozumné dediť všetky entity z jednej abstraktnej nadtriedy, len aby to bolo možné.

6.1. Mapovanie vzťahu s @Akýkoľvek

Tu je príklad, ako môžeme definovať odkaz na každú entitu, ktorá implementuje Serializovateľné (t. j. akémukoľvek subjektu vôbec):

@Entity verejná trieda EntityDescription implementuje Serializable {private String description; @Any (metaDef = "EntityDescriptionMetaDef", metaColumn = @Column (name = "entity_type")) @JoinColumn (name = "entity_id") súkromná serializovateľná entita; }

The metaDef property je názov definície a metaColumn je názov stĺpca, ktorý sa použije na rozlíšenie typu entity (na rozdiel od stĺpca diskriminátora v mapovaní hierarchie jednej tabuľky).

Tiež určíme stĺpec, ktorý bude odkazovať na id subjektu. Stojí za zmienku, že tento stĺpec nebude cudzím kľúčom pretože môže odkazovať na ktorúkoľvek tabuľku, ktorú chceme.

The entity_id stĺpec tiež nemôže byť vo všeobecnosti jedinečný, pretože rôzne tabuľky môžu mať opakované identifikátory.

The typ_typu/entity_id pár by však mal byť jedinečný, pretože jedinečne popisuje entitu, na ktorú odkazujeme.

6.2. Definovanie @Akýkoľvek Mapovanie pomocou @AnyMetaDef

Momentálne Hibernate nevie, ako rozlíšiť rôzne typy entít, pretože sme nešpecifikovali, čo typ_typu stĺpec môže obsahovať.

Aby to fungovalo, musíme pridať meta-definíciu mapovania pomocou @AnyMetaDef anotácia. Najlepšie to urobíme na úrovni balíka, aby sme ho mohli znova použiť v iných mapovaniach.

Tu je postup, ako balíček-info.java súbor s @AnyMetaDef anotácia bude vyzerať takto:

@AnyMetaDef (name = "EntityDescriptionMetaDef", metaType = "string", idType = "int", metaValues ​​= {@MetaValue (hodnota = "zamestnanec", targetEntity = Employee.class), @MetaValue (hodnota = "telefón", targetEntity) = Phone.class)}) balíček com.baeldung.hibernate.pojo;

Tu sme špecifikovali typ súboru typ_typu stĺpec (povrázok), typ entity_id stĺpec (int), prijateľné hodnoty v typ_typu stĺpec („Zamestnanec“ a „Telefón“) a zodpovedajúce typy entít.

Predpokladajme, že máme zamestnanca s dvomi takto opísanými telefónmi:

Zamestnanec zamestnanec = nový zamestnanec (); Telefón phone1 = nový telefón („555-45-67“); Telefón phone2 = nový telefón („555-89-01“); employee.getPhones (). add (phone1); employee.getPhones (). add (phone2);

Teraz by sme mohli pridať popisné metadáta ku všetkým trom entitám, aj keď majú rôzne nepríbuzné typy:

EntityDescription employeeDescription = nový EntityDescription („Odoslať na konferenciu budúci rok“, zamestnanec); EntityDescription phone1Description = nový EntityDescription ("Domáci telefón (nevolať po 22:00)", telefón1); EntityDescription phone2Description = nový EntityDescription ("Pracovný telefón", telefón1);

7. Záver

V tomto článku sme preskúmali niektoré z poznámok Hibernate, ktoré umožňujú doladenie mapovania entít pomocou nespracovaného jazyka SQL.

Zdrojový kód článku je k dispozícii na GitHub.


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