Vedvarende Enums i JPA

Java Top

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 KURSET

1. 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

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 KURSET

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