Vedvarende Enums i JPA
Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:
>> KONTROLLER KURSET1. Introduksjon
I JPA versjon 2.0 og nyere er det ingen praktisk måte å kartlegge Enum-verdier til en databasekolonne. Hvert alternativ har sine begrensninger og ulemper. Disse problemene kan unngås ved å bruke JPA 2.1. funksjoner.
I denne opplæringen vil vi se på de forskjellige mulighetene vi har for å vedvare enums i en database ved hjelp av JPA. Vi vil også beskrive fordeler og ulemper, samt gi enkle kodeeksempler.
2. Bruke @Nummerert Kommentar
Det vanligste alternativet for å kartlegge en enumverdi til og fra databasepresentasjonen i JPA før 2.1. er å bruke @Nummerert kommentar. På denne måten kan vi instruere en JPA-leverandør om å konvertere en enum til sin ordinal eller String verdi.
Vi vil utforske begge alternativene i denne delen.
Men først, la oss lage en enkel @Enhet som vi skal bruke gjennom denne opplæringen:
@Entity public class Article {@Id private int id; privat strengetittel; // standardkonstruktører, getters og settere}
2.1. Kartlegging av ordinær verdi
Hvis vi legger @Enumerated (EnumType.ORDINAL) merknader på enum-feltet, vil JPA bruke Enum.ordinal () verdi når du vedvarer en gitt enhet i databasen.
La oss introdusere den første enum:
offentlig enum Status {ÅPEN, ANMELDELSE, GODKJENT, AVVIST; }
La oss deretter legge det til Artikkel klasse og kommentere den med @Enumerated (EnumType.ORDINAL):
@Entity public class Article {@Id private int id; privat strengetittel; @Enumerated (EnumType.ORDINAL) privat status status; }
Nå, når du vedvarer en Artikkel enhet:
Artikkelartikkel = ny artikkel (); article.setId (1); article.setTitle ("ordinal title"); article.setStatus (Status.OPEN);
JPA vil utløse følgende SQL-setning:
sett inn i artikkel (status, tittel, id) verdier (?,?,?) bindende parameter [1] som [INTEGER] - [0] bindingsparameter [2] som [VARCHAR] - [ordinær tittel] bindingsparameter [3] som [INTEGER] - [1]
Et problem med denne typen kartlegging oppstår når vi trenger å endre vår oppsummering. Hvis vi legger til en ny verdi i midten eller omorganiserer enumens rekkefølge, bryter vi den eksisterende datamodellen.
Slike problemer kan være vanskelig å fange, så vel som problematiske å fikse, da vi må oppdatere alle databaseregistrene.
2.2. Kartlegging av strengverdi
Analogt vil JPA bruke Enum.name () verdi når du lagrer en enhet hvis vi kommenterer enum-feltet med @Enumerated (EnumType.STRING).
La oss lage den andre enum:
offentlig enum Type {INTERN, EXTERNAL; }
Og la oss legge det til vårt Artikkel klasse og kommentere den med @Enumerated (EnumType.STRING):
@Entity public class Article {@Id private int id; privat strengetittel; @Enumerated (EnumType.ORDINAL) privat status status; @Enumerated (EnumType.STRING) privat Type type; }
Nå, når du vedvarer en Artikkel enhet:
Artikkelartikkel = ny artikkel (); article.setId (2); article.setTitle ("strengetittel"); article.setType (Type.EXTERNAL);
JPA vil utføre følgende SQL-setning:
sett inn i artikkel (status, tittel, type, id) verdier (?,?,?,?) bindende parameter [1] som [INTEGER] - [null] bindende parameter [2] som [VARCHAR] - [streng tittel] binding parameter [3] som [VARCHAR] - [EXTERNAL] binding parameter [4] as [INTEGER] - [2]
Med @Enumerated (EnumType.STRING), kan vi trygt legge til nye enumverdier eller endre enums rekkefølge. Imidlertid vil omdøping av enumverdi fortsatt bryte databasedataene.
I tillegg, selv om denne datarepresentasjonen er langt mer lesbar sammenlignet med @Enumerated (EnumType.ORDINAL) alternativet, det bruker også mye mer plass enn nødvendig. Dette kan vise seg å være et viktig problem når vi trenger å håndtere et stort datamengde.
3. Bruke @PostLoad og @PrePersist Kommentarer
Et annet alternativ vi har å håndtere vedvarende enums i en database, er å bruke standard JPA-tilbakeringingsmetoder. Vi kan kartlegge enumene våre frem og tilbake i @PostLoad og @PrePersist arrangementer.
Tanken er å ha to attributter i en enhet. Den første er tilordnet en databaseverdi, og den andre er en @Flyktig felt som har en reell enumverdi. Forbigående attributt brukes deretter av forretningslogikkoden.
For å bedre forstå konseptet, la oss lage et nytt enum og bruke det int verdi i kartleggingslogikken:
offentlig enum Prioritet {LAV (100), MEDIUM (200), HØY (300); privat int prioritet; privat prioritet (int prioritet) {this.priority = prioritet; } public int getPriority () {returprioritet; } offentlig statisk prioritet for (int-prioritet) {retur Stream.of (Priority.values ()) .filter (p -> p.getPriority () == prioritet) .findFirst () .orElseThrow (IllegalArgumentException :: new); }}
Vi har også lagt til Priority.of () metode for å gjøre det enkelt å få en Prioritet forekomst basert på dens int verdi.
Nå, for å bruke den i vår Artikkel klasse, må vi legge til to attributter og implementere tilbakekallingsmetoder:
@Entity public class Article {@Id private int id; privat strengetittel; @Enumerated (EnumType.ORDINAL) privat status status; @Enumerated (EnumType.STRING) privat Type type; @Basic private int prioriteringsverdi; @ Transient privat prioritetsprioritet; @PostLoad ugyldig fillTransient () {if (priorityValue> 0) {this.priority = Priority.of (priorityValue); }} @PrePersist ugyldig fillPersistent () {if (prioritet! = Null) {this.priorityValue = prioritet.getPriority (); }}}
Nå, når du vedvarer en Artikkel enhet:
Artikkelartikkel = ny artikkel (); article.setId (3); article.setTitle ("tilbakeringingstittel"); article.setPriority (Priority.HIGH);
JPA vil utløse følgende SQL-spørring:
sett inn i Article (prioritetsverdi, status, tittel, type, id) verdier (?,?,?,?,?) bindende parameter [1] som [INTEGER] - [300] bindingsparameter [2] som [INTEGER] - [ null] binding parameter [3] as [VARCHAR] - [callback title] binding parameter [4] as [VARCHAR] - [null] binding parameter [5] as [INTEGER] - [3]
Selv om dette alternativet gir oss mer fleksibilitet i valg av representasjon av databaseverdien sammenlignet med tidligere beskrevne løsninger, er det ikke ideelt. Det føles bare ikke riktig å ha to attributter som representerer en enkelt enum i enheten. I tillegg, hvis vi bruker denne typen kartlegging, kan vi ikke bruke enums verdi i JPQL-spørsmål.
4. Bruke JPA 2.1 @Konverterer Kommentar
For å overvinne begrensningene til løsningene som er vist ovenfor, introduserte JPA 2.1 release en ny standardisert API som kan brukes til å konvertere et enhetsattributt til en databaseverdi og omvendt. Alt vi trenger å gjøre er å lage en ny klasse som implementeres javax.persistence.AttributeConverter og kommentere den med @Konverterer.
La oss se et praktisk eksempel. Men først, som vanlig, lager vi et nytt enum:
offentlig enum Kategori {SPORT ("S"), MUSIC ("M"), TECHNOLOGY ("T"); privat streng kode; privat kategori (strengkode) {this.code = code; } offentlig String getCode () {returkode; }}
Vi må også legge den til Artikkel klasse:
@Entity public class Article {@Id private int id; privat strengetittel; @Enumerated (EnumType.ORDINAL) privat status status; @Enumerated (EnumType.STRING) privat Type type; @Basic private int prioriteringsverdi; @ Transient privat prioritetsprioritet; privat kategorikategori; }
La oss lage en ny CategoryConverter:
@Converter (autoApply = true) Offentlig klasse CategoryConverter implementerer AttributeConverter {@Override public String convertToDatabaseColumn (Kategorikategori) {if (category == null) {return null; } returner category.getCode (); } @ Override public Category convertToEntityAttribute (strengkode) {if (code == null) {return null; } returner Stream.of (Category.values ()) .filter (c -> c.getCode (). tilsvarer (code)) .findFirst () .orElseThrow (IllegalArgumentException :: new); }}
Vi har satt inn @Konverterer’S verdi av søk automatisk til ekte slik at JPA automatisk bruker konverteringslogikken på alle tilordnede attributter til a Kategori type. Ellers måtte vi sette @Konverterer kommentar direkte på enhetens felt.
La oss nå fortsette en Artikkel enhet:
Artikkelartikkel = ny artikkel (); article.setId (4); article.setTitle ("konvertert tittel"); article.setCategory (Category.MUSIC);
Deretter vil JPA utføre følgende SQL-setning:
sett inn i artikkel (kategori, prioritetsverdi, status, tittel, type, id) verdier (?,?,?,?,?,?) Konvertert verdi ved binding: MUSIC -> M binding parameter [1] som [VARCHAR] - [ M] bindingsparameter [2] som [INTEGER] - [0] bindingsparameter [3] som [INTEGER] - [null] bindingsparameter [4] som [VARCHAR] - [konvertert tittel] bindingsparameter [5] som [VARCHAR ] - [null] bindingsparameter [6] som [INTEGER] - [4]
Som vi kan se, kan vi ganske enkelt sette våre egne regler for å konvertere enums til en tilsvarende databaseverdi hvis vi bruker AttributeConverter grensesnitt. Videre kan vi trygt legge til nye enumverdier eller endre de eksisterende uten å bryte allerede vedvarende data.
Den overordnede løsningen er enkel å implementere og adresserer alle ulempene med alternativene som er presentert i de tidligere avsnittene.
5. Bruke Enums i JPQL
La oss nå se hvor enkelt det er å bruke enums i JPQL-spørringene.
For å finne alt Artikkel enheter med Kategori.SPORT kategori, må vi utføre følgende uttalelse:
String jpql = "velg en fra artikkel a der a.category = com.baeldung.jpa.enums.Category.SPORT"; Liste artikler = em.createQuery (jpql, Article.class) .getResultList ();
Det er viktig å merke seg at i dette tilfellet må vi bruke et fullt kvalifisert enumnavn.
Selvfølgelig er vi ikke begrenset til statiske spørsmål. Det er helt lovlig å bruke de nevnte parametrene:
String jpql = "velg en fra artikkel a der a.category =: category"; TypedQuery spørring = em.createQuery (jpql, Article.class); query.setParameter ("kategori", Category.TECHNOLOGY); Listeartikler = query.getResultList ();
Ovenstående eksempel presenterer en veldig praktisk måte å danne dynamiske spørsmål på.
I tillegg trenger vi ikke bruke fullt kvalifiserte navn.
6. Konklusjon
I denne opplæringen har vi dekket forskjellige måter å vedvare enumverdier i en database. Vi har presentert alternativer vi har når du bruker JPA i versjon 2.0 og nyere, samt en ny API tilgjengelig i JPA 2.1 og nyere.
Det er verdt å merke seg at dette ikke er de eneste mulighetene for å håndtere enums i JPA. Noen databaser, som PostgreSQL, gir en dedikert kolonnetype for å lagre enumverdier. Imidlertid er slike løsninger utenfor omfanget av denne artikkelen.
Som en tommelfingerregel skal vi alltid bruke AttributeConverter grensesnitt og @Konverterer kommentar hvis vi bruker JPA 2.1 eller nyere.
Som vanlig er alle kodeeksemplene tilgjengelige på GitHub-depotet vårt.
Java bunn