Brugerdefinerede typer i dvale og @type-kommentar

1. Oversigt

Dvaletilstand forenkler datahåndtering mellem SQL og JDBC ved at kortlægge den objektorienterede model i Java med den relationelle model i databaser. Selvom kortlægning af grundlæggende Java-klasser er indbygget i dvale, kortlægning af brugerdefinerede typer er ofte kompleks.

I denne vejledning ser vi, hvordan Dvaletilstand giver os mulighed for at udvide den grundlæggende typekortlægning til tilpassede Java-klasser. Derudover vil vi også se nogle almindelige eksempler på brugerdefinerede typer og implementere dem ved hjælp af Hibernates type kortlægningsmekanisme.

2. Typer af dvale-kortlægning

Dvaletilstand bruger kortlægningstyper til konvertering af Java-objekter til SQL-forespørgsler til lagring af data. På samme måde bruger den kortlægningstyper til konvertering af SQL ResultSet til Java-objekter, mens data hentes.

Generelt kategoriserer dvale typerne i enhedstyper og værdityper. Specifikt bruges enhedstyper til at kortlægge domænespecifikke Java-enheder og eksisterer derfor uafhængigt af andre typer i applikationen. I modsætning hertil bruges værdityper til at kortlægge dataobjekter i stedet og ejes næsten altid af enhederne.

I denne vejledning vil vi fokusere på kortlægningen af ​​værdityper, som yderligere klassificeres i:

  • Grundlæggende typer - Kortlægning af grundlæggende Java-typer
  • Kan integreres - Kortlægning for sammensatte java-typer / POJO'er
  • Samlinger - Kortlægning for en samling af grundlæggende og sammensat java-type

3. Maven-afhængigheder

For at oprette vores brugerdefinerede dvale-typer har vi brug for dvale-kerneafhængigheden:

 org. dvale-dvale-core 5.3.6.Final 

4. Brugerdefinerede typer i dvale

Vi kan bruge grundlæggende kortlægningstyper i dvale til de fleste brugerdomæner. Der er dog mange brugssager, hvor vi har brug for at implementere en brugerdefineret type.

Dvaletilstand gør det relativt lettere at implementere brugerdefinerede typer. Der er tre tilgange til implementering af en brugerdefineret type i dvale. Lad os diskutere hver af dem i detaljer.

4.1. Implementerer BasicType

Vi kan oprette en brugerdefineret basistype ved at implementere dvale BasicType eller en af ​​dens specifikke implementeringer, AbstractSingleColumnStandardBasicType.

Før vi implementerer vores første brugerdefinerede type, skal vi se en almindelig brugssag til implementering af en basistype. Antag, at vi skal arbejde med en ældre database, der gemmer datoer som VARCHAR. Normalt, Dvaletilstand vil kortlægge dette til Snor Java-type. Dermed bliver datavalidering sværere for applikationsudviklere.

Så lad os implementere vores LocalDateString type, der gemmer LocalDate Java-type som VARCHAR:

offentlig klasse LocalDateStringType udvider AbstractSingleColumnStandardBasicType {offentlig statisk endelig LocalDateStringType INSTANCE = ny LocalDateStringType (); offentlig LocalDateStringType () {super (VarcharTypeDescriptor.INSTANCE, LocalDateStringJavaDescriptor.INSTANCE); } @ Override public String getName () {returner "LocalDateString"; }}

Det vigtigste i denne kode er konstruktorparametrene. For det første er en forekomst af SqlTypeDescriptor, som er dvaleens SQL-repræsentation, som er VARCHAR for vores eksempel. Og det andet argument er en forekomst af JavaTypeDescriptor som repræsenterer Java-typen.

Nu kan vi implementere en LocalDateStringJavaDescriptor til opbevaring og hentning LocalDate som VARCHAR:

offentlig klasse LocalDateStringJavaDescriptor udvider AbstractTypeDescriptor {offentlig statisk endelig LocalDateStringJavaDescriptor INSTANCE = ny LocalDateStringJavaDescriptor (); offentlig LocalDateStringJavaDescriptor () {super (LocalDate.class, ImmutableMutabilityPlan.INSTANCE); } // andre metoder}

Dernæst er vi nødt til at tilsidesætte indpakning og pakke ud metoder til konvertering af Java-typen til SQL. Lad os starte med pakke ud:

@ Override offentlig X udpakning (LocalDate-værdi, Klassetype, WrapperOptions-indstillinger) {hvis (værdi == null) returnerer null; hvis (String.class.isAssignableFrom (type)) return (X) LocalDateType.FORMATTER.format (værdi); kaste ukendt Uindpakning (type); }

Dernæst indpakning metode:

@Override public LocalDate wrap (X-værdi, WrapperOptions-indstillinger) {if (værdi == null) returnerer null; hvis (String.class.isInstance (værdi)) returnerer LocalDate.from (LocalDateType.FORMATTER.parse ((CharSequence) værdi)); kaste unknownWrap (value.getClass ()); }

pakke ud () kaldes under PreparedStatement bindende at konvertere LocalDate til en strengtype, der er kortlagt til VARCHAR. Ligeledes, wrap () kaldes under ResultSet hentning til konvertering Snor til en Java LocalDate.

Endelig kan vi bruge vores tilpassede type i vores enhedsklasse:

@Entity @Table (name = "OfficeEmployee") offentlig klasse OfficeEmployee {@Column @Type (type = "com.baeldung.hibernate.customtypes.LocalDateStringType") privat LocalDate dateOfJoining; // andre felter og metoder}

Senere ser vi, hvordan vi kan registrere denne type i dvale. Og som et resultat henvis til denne type ved hjælp af registreringsnøglen i stedet for det fuldt kvalificerede klassenavn.

4.2. Implementerer Brugertype

Med de mange basistyper i dvale er det meget sjældent, at vi har brug for at implementere en brugerdefineret basistype. I modsætning hertil er en mere typisk brugssag at kortlægge et komplekst Java-domæneobjekt til databasen. Sådanne domæneobjekter er generelt gemt i flere databasekolonner.

Så lad os implementere et kompleks Telefonnummer gøre indsigelse ved at implementere Brugertype:

offentlig klasse PhoneNumberType implementerer UserType {@Override public int [] sqlTypes () {return new int [] {Types.INTEGER, Types.INTEGER, Types.INTEGER}; } @Override public Class returnClass () {return PhoneNumber.class; } // andre metoder} 

Her, den tilsidesatte sqlTypes metode returnerer SQL-typerne af felter i samme rækkefølge som de er deklareret i vores Telefonnummer klasse. Tilsvarende returnerede klasse metode returnerer vores Telefonnummer Java-type.

Det eneste der er tilbage at gøre er at implementere metoderne til at konvertere mellem Java-typen og SQL-typen, som vi gjorde for vores BasicType.

Først nullSafeGet metode:

@Override public Object nullSafeGet (ResultSet rs, String [] names, SharedSessionContractImplementor session, Object owner) kaster HibernateException, SQLException {int countryCode = rs.getInt (names [0]); hvis (rs.wasNull ()) returnerer null; int cityCode = rs.getInt (navne [1]); int antal = rs.getInt (navne [2]); PhoneNumber medarbejderNummer = nyt Telefonnummer (landskode, bykode, nummer); returnere medarbejdernummer }

Dernæst nullSafeSet metode:

@Override public void nullSafeSet (PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throw HibernateException, SQLException {if (Objects.isNull (value)) {st.setNull (index, Types.INTEGER); st.setNull (indeks + 1, Typer.INTEGER); st.setNull (indeks + 2, Typer.INTEGER); } ellers {PhoneNumber employeeNumber = (PhoneNumber) værdi; st.setInt (indeks, medarbejderNummer.getCountryCode ()); st.setInt (indeks + 1, medarbejderNummer.getCityCode ()); st.setInt (indeks + 2, medarbejderNummer.getNummer ()); }}

Endelig kan vi erklære vores skik PhoneNumberType i vores Kontor Medarbejder enhedsklasse:

@Entity @Table (navn = "OfficeMedarbejder") offentlig klasse OfficeMedarbejder {@Kolonner (kolonner = {@Kolonne (navn = "land_kode"), @Kolonne (navn = "by_kode"), @Kolonne (navn = "nummer") }) @Type (type = "com.baeldung.hibernate.customtypes.PhoneNumberType") privat PhoneNumber medarbejderNummer; // andre felter og metoder}

4.3. Implementerer CompositeUserType

Implementerer Brugertype fungerer godt til ligefremme typer. Kortlægning af komplekse Java-typer (med samlinger og kaskadede sammensatte typer) kræver imidlertid mere sofistikering. Dvaletilstand giver os mulighed for at kortlægge sådanne typer ved at implementere CompositeUserType interface.

Så lad os se dette i aktion ved at implementere en Adressetype til Kontor Medarbejder enhed, vi brugte tidligere:

public class AddressType implementerer CompositeUserType {@Override public String [] getPropertyNames () {return new String [] {"addressLine1", "addressLine2", "city", "country", "zipcode"}; } @ Override public Type [] getPropertyTypes () {returner ny Type [] {StringType.INSTANCE, StringType.INSTANCE, StringType.INSTANCE, StringType.INSTANCE, IntegerType.INSTANCE}; } // andre metoder}

I modsætning til Brugertyper, som kortlægger indekset over typegenskaberne, CompositeType kortlægger ejendomsnavne på vores Adresse klasse. Vigtigere, getPropertyType metoden returnerer kortlægningstyperne for hver ejendom.

Derudover skal vi også implementere getPropertyValue og setPropertyValue metoder til kortlægning PreparedStatement og ResultSet indekser for at skrive ejendom. Overvej som et eksempel getPropertyValue for vores Adressetype:

@ Override public Object getPropertyValue (Objektkomponent, int-egenskab) kaster HibernateException {Adresse empAdd = (Adresse) komponent; switch (ejendom) {case 0: return empAdd.getAddressLine1 (); sag 1: returner empAdd.getAddressLine2 (); tilfælde 2: returner empAdd.getCity (); tilfælde 3: returner empAdd.getCountry (); tilfælde 4: returner Integer.valueOf (empAdd.getZipCode ()); } smid nyt IllegalArgumentException (egenskab + "er et ugyldigt egenskabsindeks for klassetype" + component.getClass (). getName ()); }

Endelig skal vi gennemføre nullSafeGet og nullSafeSet metoder til konvertering mellem Java og SQL-typer. Dette svarer til det, vi gjorde tidligere i vores PhoneNumberType.

Bemærk, at CompositeType'S implementeres generelt som en alternativ kortlægningsmekanisme til Kan integreres typer.

4.4. Type Parameterisering

Udover at oprette tilpassede typer, Dvale giver os også mulighed for at ændre adfærden for typer baseret på parametre.

Antag for eksempel, at vi skal gemme Løn for vores Kontor Medarbejder. Endnu vigtigere skal ansøgningen konvertere lønbeløbettil geografisk beløb i lokal valuta.

Så lad os implementere vores parametriserede LønType som accepterer betalingsmiddel som parameter:

offentlig klasse SalaryType implementerer CompositeUserType, DynamicParameterizedType {private String localCurrency; @Override public void setParameterValues ​​(Egenskabsparametre) {this.localCurrency = parameters.getProperty ("valuta"); } // andre implementeringsmetoder fra CompositeUserType}

Bemærk, at vi har sprunget over CompositeUserType metoder fra vores eksempel til at fokusere på parametrering. Her implementerede vi simpelthen dvale DynamicParameterizedType, og tilsidesæt setParameterValues ​​() metode. Nu, den LønType acceptere en betalingsmiddel parameter og konverterer ethvert beløb, før det gemmes.

Vi passerer betalingsmiddel som en parameter, mens deklarerer Løn:

@Entity @Table (name = "OfficeEmployee") offentlig klasse OfficeEmployee {@Type (type = "com.baeldung.hibernate.customtypes.SalaryType", parameters = {@Parameter (name = "currency", value = "USD") }) @Kolonner (kolonner = {@Kolonne (navn = "beløb"), @Kolonne (navn = "valuta")}) privat Lønløn; // andre felter og metoder}

5. Grundlæggende type register

Dvaletilstand opretholder kortlægningen af ​​alle indbyggede basistyper i BasicTypeRegistry. Således elimineres behovet for at kommentere kortoplysninger for sådanne typer.

Derudover giver Hibernate os mulighed for at registrere brugerdefinerede typer, ligesom grundlæggende typer, i BasicTypeRegistry. Normalt registrerer applikationer brugerdefineret type, mens bootstrapping af SessionFactory. Lad os forstå dette ved at registrere LocalDateString type, vi implementerede tidligere:

privat statisk SessionFactory makeSessionFactory () {ServiceRegistry serviceRegistry = StandardServiceRegistryBuilder () .applySettings (getProperties ()). build (); MetadataSources metadataSources = nye MetadataSources (serviceRegistry); Metadata metadata = metadataSources.getMetadataBuilder () .applyBasicType (LocalDateStringType.INSTANCE) .build (); returner metadata.getSessionFactoryBuilder (). build ()} private statiske egenskaber getProperties () {// returner dvaleegenskaber}

Dermed, det fjerner begrænsningen ved at bruge det fuldt kvalificerede klassenavn i typekortlægning:

@Entity @Table (name = "OfficeEmployee") offentlig klasse OfficeEmployee {@Column @Type (type = "LocalDateString") privat LocalDate dateOfJoining; // andre metoder}

Her, LocalDateString er nøglen, som LocalDateStringType er kortlagt.

Alternativt kan vi springe type registrering over ved at definere TypeDefs:

@TypeDef (name = "PhoneNumber", typeClass = PhoneNumberType.class, defaultForType = PhoneNumber.class) @Entity @Table (name = "OfficeEmployee") offentlig klasse OfficeEmployee {@Columns (kolonner = {@Column (name = "country_code") ), @Column (name = "city_code"), @Column (name = "number")}) privat PhoneNumber medarbejderNummer; // andre metoder}

6. Konklusion

I denne vejledning diskuterede vi flere tilgange til at definere en brugerdefineret type i dvale. Derudover Vi implementerede et par brugerdefinerede typer til vores enhedsklasse baseret på nogle almindelige brugssager, hvor en ny brugerdefineret type kan komme til nytte.

Som altid er kodeeksemplerne tilgængelige på GitHub.


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