Ustrajni enumi u JPA
Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:
>> PROVJERITE TEČAJ1. Uvod
U JPA verziji 2.0 i starijim, ne postoji prikladan način za mapiranje vrijednosti Enum u stupac baze podataka. Svaka opcija ima svoja ograničenja i nedostatke. Ova se pitanja mogu izbjeći korištenjem JPA 2.1. značajke.
U ovom uputstvu ćemo pogledati različite mogućnosti koje imamo kako bismo nastavili nabrajati u bazi podataka koristeći JPA. Također ćemo opisati njihove prednosti i nedostatke, kao i jednostavne primjere koda.
2. Korištenje @Nabrojeno Bilješka
Najčešća opcija mapiranja vrijednosti nabrajanja u i iz njezine baze podataka u JPA prije 2.1. je koristiti @Nabrojeno bilješka. Na ovaj način možemo uputiti JPA davatelja da pretvori enum u njegov redni ili Niz vrijednost.
Istražit ćemo obje mogućnosti u ovom odjeljku.
Ali prvo, stvorimo jednostavan @ Entitet koje ćemo koristiti tijekom ovog vodiča:
Članak o javnoj klasi entiteta {@Id private int id; privatni naslov niza; // standardni konstruktori, getteri i postavljači}
2.1. Mapiranje redne vrijednosti
Ako stavimo @Enumerated (EnumType.ORDINAL) napomena na polju nabrajanja, JPA će koristiti Enum.ordinal () vrijednost kod trajnog zadatka entiteta u bazi podataka.
Uvedimo prvi nabrajanje:
status javnog popisa {OTVORENO, PREGLEDATI, ODOBRENO, ODBIJENO; }
Dalje, dodajte ga u Članak razredu i označite ga s @Enumerated (EnumType.ORDINAL):
Članak o javnoj klasi entiteta {@Id private int id; privatni naslov niza; @Numerani (EnumType.ORDINAL) status privatnog statusa; }
Sada, kad se ustraje Članak entitet:
Članak u članku = novi članak (); article.setId (1); article.setTitle ("redni naslov"); article.setStatus (Status.OPEN);
JPA će pokrenuti sljedeći SQL izraz:
umetnite u članak (status, naslov, id) vrijednosti (?,?,?) parametar vezanja [1] kao [INTEGER] - [0] parametar vezanja [2] kao [VARCHAR] - [redni naslov] parametar vezanja [3] kao [INTEGER] - [1]
Problem s ovom vrstom mapiranja pojavljuje se kada moramo izmijeniti svoj nabrajanje. Ako u sredinu dodamo novu vrijednost ili preuredimo redoslijed nabrajanja, razbit ćemo postojeći model podataka.
Takve probleme može biti teško uhvatiti, ali ih je i problematično riješiti, jer bismo morali ažurirati sve zapise baze podataka.
2.2. Mapiranje vrijednosti niza
Analogno tome, JPA će koristiti Enum.name () vrijednost kod spremanja entiteta ako polje enum označimo s @Nabrojeno (EnumType.STRING).
Stvorimo drugi nabrajanje:
javni popis Tip {UNUTARNJI, VANJSKI; }
I dodajmo to našem Članak razredu i označite ga s @Nabrojeno (EnumType.STRING):
Članak o javnoj klasi entiteta {@Id private int id; privatni naslov niza; @Numerani (EnumType.ORDINAL) status privatnog statusa; @Enumerated (EnumType.STRING) tip privatnog tipa; }
Sada, kad se ustraje Članak entitet:
Članak u članku = novi članak (); article.setId (2); article.setTitle ("naslov niza"); article.setType (Type.EXTERNAL);
JPA će izvršiti sljedeći SQL izraz:
umetnite u članak (status, naslov, vrsta, id) vrijednosti (?,?,?,?) parametar vezanja [1] kao [INTEGER] - [null] parametar vezanja [2] kao [VARCHAR] - [string title] vezanje parametar [3] kao [VARCHAR] - [EXTERNAL] parametar vezanja [4] kao [INTEGER] - [2]
S @Nabrojeno (EnumType.STRING), možemo sigurno dodati nove vrijednosti nabrajanja ili promijeniti redoslijed nabrajanja. Međutim, preimenovanje vrijednosti nabrajanja i dalje će slomiti podatke baze podataka.
Uz to, iako je ovaj prikaz podataka daleko čitljiviji u odnosu na @Enumerated (EnumType.ORDINAL) opcija, također troši puno više prostora nego što je potrebno. To bi se moglo pokazati značajnim problemom kada se moramo nositi s velikom količinom podataka.
3. Korištenje @PostLoad i @PrePersist Bilješke
Druga mogućnost s kojom se moramo nositi s trajnim nabrajanjima u bazi podataka je uporaba standardnih metoda JPA povratnog poziva. Naše popise možemo mapirati naprijed-natrag u @PostLoad i @PrePersist događaja.
Ideja je imati dva atributa u entitetu. Prva se preslikava na vrijednost baze podataka, a druga je @Prijelazno polje koje ima stvarnu vrijednost nabrajanja. Privremeni atribut tada koristi kod poslovne logike.
Da bismo bolje razumjeli koncept, stvorimo novu enum i upotrijebimo je int vrijednost u logici mapiranja:
javni popis Prioritet {LOW (100), SREDNJI (200), HIGH (300); privatni int prioritet; privatni prioritet (int prioritet) {this.priority = prioritet; } public int getPriority () {prioritet povrata; } javni statički prioritet (int prioritet) {return Stream.of (Priority.values ()) .filter (p -> p.getPriority () == prioritet) .findFirst () .orElseThrow (IllegalArgumentException :: new); }}
Također smo dodali Priority.of () metoda koja olakšava dobivanje a Prioritet instanca na temelju svoje int vrijednost.
Sada, da ga koristimo u našem Članak klase, moramo dodati dva atributa i implementirati metode povratnog poziva:
Članak o javnoj klasi entiteta {@Id private int id; privatni naslov niza; @Numerani (EnumType.ORDINAL) status privatnog statusa; @Enumerated (EnumType.STRING) tip privatnog tipa; @Basic private int priorityValue; @Prijelazni privatni prioritetni prioritet; @PostLoad void fillTransient () {if (priorityValue> 0) {this.priority = Priority.of (prioritetValue); }} @PrePersist void fillPersistent () {if (prioritet! = Null) {this.priorityValue = prioritet.getPriority (); }}}
Sada, kad se ustraje Članak entitet:
Članak članak = novi članak (); article.setId (3); article.setTitle ("naslov povratnog poziva"); article.setPriority (Priority.HIGH);
JPA će pokrenuti sljedeći SQL upit:
umetnite u Article (priorityValue, status, title, type, id) vrijednosti (?,?,?,?,?) parametar vezanja [1] kao [INTEGER] - [300] parametar vezanja [2] kao [INTEGER] - [ null] parametar vezanja [3] kao [VARCHAR] - [naslov povratnog poziva] parametar vezanja [4] kao [VARCHAR] - [null] parametar vezanja [5] kao [INTEGER] - [3]
Iako nam ova opcija daje veću fleksibilnost u odabiru predstavljanja vrijednosti baze podataka u odnosu na prethodno opisana rješenja, ona nije idealna. Jednostavno se ne osjeća ispravno imati dva atributa koji predstavljaju jedan nabrajanje u entitetu. Uz to, ako koristimo ovu vrstu mapiranja, ne možemo koristiti vrijednost enum-a u JPQL upitima.
4. Korištenje JPA 2.1 @Konverter Bilješka
Da bi se prevladala ograničenja gore prikazanih rješenja, izdanje JPA 2.1 predstavilo je novi standardizirani API koji se može koristiti za pretvaranje atributa entiteta u vrijednost baze podataka i obrnuto. Sve što trebamo učiniti je stvoriti novu klasu koja se provodi javax.persistence.AttributeConverter i zabilježite ga s @Konverter.
Pogledajmo praktični primjer. Ali prvo, kao i obično, stvorit ćemo novi nabrajanje:
kategorija javnog popisa {SPORT ("S"), GLAZBA ("M"), TEHNOLOGIJA ("T"); kod privatnog niza; privatna kategorija (kod niza) {this.code = code; } javni String getCode () {povratni kôd; }}
Također ga moramo dodati u Članak razred:
Članak o javnoj klasi entiteta {@Id private int id; privatni naslov niza; @Numerani (EnumType.ORDINAL) status privatnog statusa; @Enumerated (EnumType.STRING) tip privatnog tipa; @Basic private int priorityValue; @Prijelazni privatni prioritetni prioritet; kategorija privatne kategorije; }
Ajmo sada stvoriti novi Pretvarač kategorija:
@Converter (autoApply = true) javna klasa CategoryConverter implementira AttributeConverter {@Override public String convertToDatabaseColumn (kategorija kategorije) {if (category == null) {return null; } vratiti kategoriju.getCode (); } @Override javna kategorija convertToEntityAttribute (kod niza) {if (code == null) {return null; } return Stream.of (Category.values ()) .filter (c -> c.getCode (). jednako (kod)) .findFirst () .orElseThrow (IllegalArgumentException :: new); }}
Postavili smo @Konverter'S vrijednost od autoPrijavi do pravi tako da će JPA automatski primijeniti logiku pretvorbe na sve preslikane atribute a Kategorija tip. Inače bismo morali staviti @Konverter napomena izravno na polju entiteta.
Ustrajmo sada Članak entitet:
Članak u članku = novi članak (); article.setId (4); article.setTitle ("pretvoreni naslov"); article.setCategory (Category.MUSIC);
Tada će JPA izvršiti sljedeći SQL izraz:
umetnuti u članak (kategorija, prioritetVrijednost, status, naslov, vrsta, id) vrijednosti (?,?,?,?,?,?) Pretvorena vrijednost na vezivanju: GLAZBA -> M parametar vezivanja [1] kao [VARCHAR] - [ M] parametar vezanja [2] kao [INTEGER] - [0] parametar vezanja [3] kao [INTEGER] - [null] parametar vezanja [4] kao [VARCHAR] - [pretvoreni naslov] parametar vezanja [5] kao [VARCHAR ] - [null] parametar vezanja [6] kao [INTEGER] - [4]
Kao što vidimo, možemo jednostavno postaviti vlastita pravila pretvaranja enuma u odgovarajuću vrijednost baze podataka ako koristimo AttributeConverter sučelje. Štoviše, možemo sigurno dodati nove vrijednosti nabrajanja ili promijeniti postojeće bez razbijanja već zadržanih podataka.
Cjelokupno rješenje jednostavno je implementirati i rješava sve nedostatke opcija predstavljenih u ranijim odjeljcima.
5. Korištenje Enumsa u JPQL-u
Pogledajmo sada kako je lako koristiti enume u JPQL upitima.
Da biste pronašli sve Članak entiteti sa Kategorija.SPORT kategoriji, moramo izvršiti sljedeću izjavu:
Niz jpql = "odaberite a iz članka a gdje je a.category = com.baeldung.jpa.enums.Category.SPORT"; Popis članaka = em.createQuery (jpql, Article.class) .getResultList ();
Važno je napomenuti da u ovom slučaju moramo koristiti potpuno kvalificirano ime popisa.
Naravno, nismo ograničeni na statičke upite. Potpuno je legalno koristiti imenovane parametre:
Niz jpql = "odaberite a iz članka a gdje je a.category =: kategorija"; Upit TypedQuery = em.createQuery (jpql, Article.class); query.setParameter ("kategorija", Kategorija.TECHNOLOGIJA); Popis članaka = query.getResultList ();
Gornji primjer predstavlja vrlo prikladan način formiranja dinamičkih upita.
Uz to, ne trebamo koristiti potpuno kvalificirana imena.
6. Zaključak
U ovom smo priručniku pokrili razne načine trajnog nabrajanja vrijednosti u bazi podataka. Predstavili smo mogućnosti koje imamo pri korištenju JPA u verziji 2.0 i starijim, kao i novi API dostupan u JPA 2.1 i novijim.
Vrijedno je napomenuti da to nisu jedine mogućnosti rješavanja popisa u JPA. Neke baze podataka, poput PostgreSQL-a, pružaju namjenski tip stupca za spremanje vrijednosti nabrajanja. Međutim, takva su rješenja izvan dosega ovog članka.
Kao pravilo, uvijek bismo trebali koristiti AttributeConverter sučelje i @Konverter napomena ako koristimo JPA 2.1 ili noviji.
Kao i obično, svi primjeri koda dostupni su na našem GitHub spremištu.
Dno Java