Pretrvávajúce výpisy v JPA

Java Top

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ

1. Úvod

V JPA verzie 2.0 a nižšej neexistuje žiadny pohodlný spôsob, ako mapovať hodnoty Enum na databázový stĺpec. Každá možnosť má svoje obmedzenia a nevýhody. Týmto problémom sa dá vyhnúť použitím JPA 2.1. Vlastnosti.

V tomto tutoriáli sa pozrieme na rôzne možnosti, ktoré máme na pretrvanie enumov v databáze pomocou JPA. Popíšeme tiež ich výhody a nevýhody a poskytneme jednoduché príklady kódov.

2. Pomocou @ Vymenované Anotácia

Najbežnejšia možnosť na mapovanie hodnoty enum do a zo svojej databázovej reprezentácie v JPA pred 2.1. je používať @ Vymenované anotácia. Týmto spôsobom môžeme dať poskytovateľovi JPA pokyn, aby konvertoval enum na svoje ordinálne alebo String hodnotu.

V tejto časti preskúmame obe možnosti.

Najprv si však vytvoríme jednoduchý @Entity ktoré budeme používať v tomto výučbe:

@Entity public class Article {@Id private int id; súkromný názov reťazca; // štandardné konštruktory, getre a setre}

2.1. Mapovanie radovej hodnoty

Keby sme dali @Enumerated (EnumType.ORDINAL) anotáciu v poli enum, JPA použije Enum.ordinal () hodnota pri pretrvávaní danej entity v databáze.

Poďme si predstaviť prvé enum:

public enum Status {OTVORENÉ, RECENZIE, SCHVÁLENÉ, ZAMIETNUTÉ; }

Ďalej to pridajme do Článok triedy a anotovať to @Enumerated (EnumType.ORDINAL):

@Entity public class Article {@Id private int id; súkromný názov reťazca; @Enumerated (EnumType.ORDINAL) súkromný stav; }

Teraz, keď pretrváva Článok subjekt:

Article article = nový článok (); article.setId (1); article.setTitle ("radový názov"); article.setStatus (Status.OPEN); 

JPA spustí nasledujúci príkaz SQL:

vložte do článku (status, title, id) hodnoty (?,?,?) parameter väzby [1] ako [INTEGER] - [0] parameter väzby [2] ako [VARCHAR] - [radový názov] parameter väzby [3] ako [INTEGER] - [1]

Problém s týmto typom mapovania nastáva, keď potrebujeme upraviť náš výčet. Ak do stredu pridáme novú hodnotu alebo zmeníme poradie výčtu, rozbijeme existujúci dátový model.

Takéto problémy môžu byť ťažko zachytiteľné a rovnako problematické ich vyriešiť, pretože by sme museli aktualizovať všetky záznamy v databáze.

2.2. Mapovanie reťazcovej hodnoty

Analogicky bude JPA používať Enum.name () hodnota pri ukladaní entity, ak anotujeme pole enum pomocou @Enumerated (EnumType.STRING).

Vytvorme druhé enum:

public enum Type {INTERNAL, EXTERNAL; }

A pridajme to k nášmu Článok triedy a anotovať to @Enumerated (EnumType.STRING):

@Entity public class Article {@Id private int id; súkromný názov reťazca; @Enumerated (EnumType.ORDINAL) súkromný stav; @Enumerated (EnumType.STRING) typ súkromného typu; }

Teraz, keď pretrváva Článok subjekt:

Article article = nový článok (); article.setId (2); article.setTitle ("názov reťazca"); article.setType (Type.EXTERNAL);

JPA vykoná nasledujúci príkaz SQL:

vložte do článku (status, title, type, id) hodnoty (?,?,?,?) parameter väzby [1] ako [INTEGER] - [null] parameter väzby [2] ako [VARCHAR] - [názov reťazca] väzba parameter [3] ako [VARCHAR] - [EXTERNAL] väzbový parameter [4] ako [INTEGER] - [2]

S @Enumerated (EnumType.STRING), môžeme bezpečne pridať nové hodnoty výčtu alebo zmeniť poradie nášho výčtu. Premenovanie hodnoty enum však stále spôsobí zlomenie údajov databázy.

Navyše, aj keď je toto znázornenie údajov oveľa čitateľnejšie v porovnaní s údajmi @Enumerated (EnumType.ORDINAL) táto možnosť tiež spotrebuje oveľa viac miesta, ako je potrebné. To sa môže stať dôležitým problémom, keď sa potrebujeme vysporiadať s veľkým objemom údajov.

3. Používanie @PostLoad a @PrePersist Anotácie

Ďalšou možnosťou, ktorú musíme vyriešiť s pretrvávajúcimi enummi v databáze, je použitie štandardných metód spätného volania JPA. Môžeme mapovať naše výčty tam a späť v @PostLoad a @PrePersist diania.

Cieľom je mať v entite dva atribúty. Prvý je namapovaný na databázovú hodnotu a druhý je a @ Prechodné pole, ktoré má skutočnú hodnotu enum. Prechodný atribút potom použije kód obchodnej logiky.

Aby sme lepšie pochopili tento koncept, vytvorme si nový výčet a použijeme jeho int hodnota v logike mapovania:

public enum Priorita {LOW (100), STREDNÉ (200), VYSOKÉ (300); priorita súkromného int; private Priority (int priority) {this.priority = priority; } public int getPriority () {priorita návratu; } public static Priority of (int priority) {return Stream.of (Priority.values ​​()) .filter (p -> p.getPriority () == priority) .findFirst () .orElseThrow (IllegalArgumentException :: new); }}

Pridali sme tiež Priority.of () metóda, ktorá uľahčuje získanie a Priorita inštancie založené na jeho int hodnotu.

Teraz, ak ho chcete použiť v našom Článok triedy, musíme pridať dva atribúty a implementovať metódy spätného volania:

@Entity public class Article {@Id private int id; súkromný názov reťazca; @Enumerated (EnumType.ORDINAL) súkromný stav; @Enumerated (EnumType.STRING) typ súkromného typu; @Basic private int priorityValue; @ Prechodná súkromná prioritná priorita; @PostLoad void fillTransient () {if (priorityValue> 0) {this.priority = Priority.of (priorityValue); }} @PrePersist void fillPersistent () {if (priority! = Null) {this.priorityValue = priority.getPriority (); }}}

Teraz, keď pretrváva Článok subjekt:

Article article = nový článok (); article.setId (3); article.setTitle ("názov spätného volania"); article.setPriority (Priority.HIGH);

JPA spustí nasledujúci dotaz SQL:

vložte do článku (priorityValue, status, title, type, id) hodnoty (?,?,?,?,?) parameter väzby [1] ako [INTEGER] - [300] parameter väzby [2] ako [INTEGER] - [ null] parameter väzby [3] ako [VARCHAR] - [názov spätného volania] parameter väzby [4] ako [VARCHAR] - [null] parameter väzby [5] ako [INTEGER] - [3]

Aj keď nám táto možnosť poskytuje väčšiu flexibilitu pri výbere zastúpenia hodnoty databázy v porovnaní s predtým popísanými riešeniami, nie je to ideálne. Akurát sa necíti dobre, že máme v entite dva atribúty predstavujúce jeden enum. Ak navyše použijeme tento typ mapovania, nebudeme schopní použiť hodnotu enum v dotazoch JPQL.

4. Používanie JPA 2.1 @Converter Anotácia

Na prekonanie obmedzení vyššie uvedených riešení predstavilo vydanie JPA 2.1 nové štandardizované API, ktoré je možné použiť na prevod atribútu entity na hodnotu databázy a naopak. Všetko, čo musíme urobiť, je vytvoriť novú triedu, ktorá sa implementuje javax.persistence.AttributeConverter a anotovať to @Converter.

Pozrime sa na praktický príklad. Najprv však ako obvykle vytvoríme nový výčet:

verejné enum Kategória {ŠPORT ("S"), HUDBA ("M"), TECHNOLÓGIA ("T"); privátny reťazcový kód; privátna kategória (reťazcový kód) {this.code = kód; } public String getCode () {návratový kód; }}

Musíme ju tiež pridať do Článok trieda:

@Entity public class Article {@Id private int id; súkromný názov reťazca; @Enumerated (EnumType.ORDINAL) súkromný stav; @Enumerated (EnumType.STRING) typ súkromného typu; @Basic private int priorityValue; @ Prechodná súkromná prioritná priorita; kategória súkromnej kategórie; }

Poďme teraz vytvoriť nový CategoryConverter:

@Converter (autoApply = true) verejná trieda CategoryConverter implementuje AttributeConverter {@Override public String convertToDatabaseColumn (kategória kategórie) {if (category == null) {return null; } návratová kategória.getCode (); } @Override public Category convertToEntityAttribute (reťazcový kód) {if (code == null) {return null; } návrat Stream.of (Category.values ​​()) .filter (c -> c.getCode (). equals (code)) .findFirst () .orElseThrow (IllegalArgumentException :: new); }}

Nastavili sme @ConverterHodnota autoApply do pravda takže JPA automaticky použije logiku prevodu na všetky mapované atribúty a Kategória typu. Inak by sme museli dať @Converter anotáciu priamo v poli entity.

Poďme teraz trvať na Článok subjekt:

Article article = nový článok (); article.setId (4); article.setTitle ("prevedený názov"); article.setCategory (Category.MUSIC);

Potom JPA vykoná nasledujúci príkaz SQL:

vložte do článku (category, priorityValue, status, title, type, id) hodnoty (?,?,?,?,?,?) Prepočítaná hodnota na väzbe: MUSIC -> M parameter väzby [1] ako [VARCHAR] - [ M] parameter väzby [2] ako [INTEGER] - [0] parameter väzby [3] ako [INTEGER] - [null] parameter väzby [4] ako [VARCHAR] - [prevedený nadpis] parameter väzby [5] ako [VARCHAR ] - [nulový] parameter väzby [6] ako [INTEGER] - [4]

Ako vidíme, môžeme jednoducho nastaviť naše vlastné pravidlá prevodu enumov na zodpovedajúcu hodnotu databázy, ak použijeme AttributeConverter rozhranie. Okrem toho môžeme bezpečne pridať nové hodnoty výčtu alebo zmeniť existujúce bez toho, aby sme narušili už pretrvávajúce údaje.

Celkové riešenie je ľahko implementovateľné a rieši všetky nevýhody možností uvedených v predchádzajúcich častiach.

5. Používanie Enums v JPQL

Pozrime sa teraz, aké ľahké je používať enums v dotazoch JPQL.

Nájsť všetky Článok subjekty s Kategória.SPORT kategórie, musíme vykonať nasledujúce vyhlásenie:

Reťazec jpql = "vyberte z článku a, kde a.category = com.baeldung.jpa.enums.Category.SPORT"; Zoznam článkov = em.createQuery (jpql, Article.class) .getResultList ();

Je dôležité poznamenať, že v tomto prípade musíme použiť úplný názov výčtu.

Samozrejme sa neobmedzujeme iba na statické dotazy. Používanie pomenovaných parametrov je úplne legálne:

Reťazec jpql = "vyberte z článku a kde a.category =: category"; TypedQuery query = em.createQuery (jpql, Article.class); query.setParameter ("category", Category.TECHNOLOGY); Zoznam článkov = query.getResultList ();

Vyššie uvedený príklad predstavuje veľmi pohodlný spôsob vytvárania dynamických dotazov.

Ďalej nemusíme používať úplne kvalifikované mená.

6. Záver

V tomto tutoriáli sme sa venovali rôznym spôsobom pretrvávania hodnôt enum v databáze. Predstavili sme možnosti, ktoré máme pri používaní JPA vo verzii 2.0 a nižšej, ako aj nové API dostupné v JPA 2.1 a novších.

Stojí za zmienku, že to nie sú jediné možnosti riešenia enumov v JPA. Niektoré databázy, napríklad PostgreSQL, poskytujú vyhradený typ stĺpca na ukladanie hodnôt enum. Takéto riešenia však nepatria do rozsahu pôsobnosti tohto článku.

Ako základné pravidlo by sme mali vždy používať AttributeConverter rozhranie a @Converter anotácia, ak používame JPA 2.1 alebo novší.

Ako obvykle sú všetky príklady kódu k dispozícii v našom úložisku GitHub.

Java dole

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ