Egendefinerte typer i dvalemodus og @Type-merknaden

1. Oversikt

Dvalemodus forenkler datahåndtering mellom SQL og JDBC ved å kartlegge den objektorienterte modellen i Java med relasjonsmodellen i databaser. Selv om kartlegging av grunnleggende Java-klasser er innebygd i dvalemodus, kartlegging av tilpassede typer er ofte kompleks.

I denne opplæringen ser vi hvordan dvalemodus lar oss utvide den grunnleggende typekartleggingen til tilpassede Java-klasser. I tillegg til det vil vi også se noen vanlige eksempler på tilpassede typer og implementere dem ved hjelp av Hibernates typekartmekanisme.

2. Typer av dvalemodus

Dvalemodus bruker karttyper for å konvertere Java-objekter til SQL-spørringer for lagring av data. På samme måte bruker den kartleggingstyper for å konvertere SQL ResultSet til Java-objekter mens data hentes.

Vanligvis kategoriserer dvalemodus typene i enhetstyper og verdityper. Spesielt brukes enhetstyper til å kartlegge domenespesifikke Java-enheter og eksisterer derfor uavhengig av andre typer i applikasjonen. I motsetning til dette brukes verdityper til å kartlegge dataobjekter i stedet og eies nesten alltid av enhetene.

I denne opplæringen vil vi fokusere på kartleggingen av verdityper som videre klassifiseres i:

  • Grunnleggende typer - Kartlegging av grunnleggende Java-typer
  • Embeddable - Kartlegging for sammensatte Java-typer / POJO-er
  • Samlinger - Kartlegging for en samling av grunnleggende og sammensatt java-type

3. Maven-avhengigheter

For å lage våre egendefinerte dvaletypetyper trenger vi avhengigheten av dvalemodus:

 org. dvale-dvalekjerne 5.3.6.Final 

4. Egendefinerte typer i dvale

Vi kan bruke grunnleggende karttyper for dvalemodus for de fleste brukerdomener. Imidlertid er det mange bruksområder der vi trenger å implementere en tilpasset type.

Dvalemodus gjør det relativt enklere å implementere tilpassede typer. Det er tre tilnærminger for å implementere en tilpasset type i dvale. La oss diskutere hver av dem i detalj.

4.1. Implementering BasicType

Vi kan lage en tilpasset grunnleggende type ved å implementere dvalemodus BasicType eller en av dens spesifikke implementeringer, AbstractSingleColumnStandardBasicType.

Før vi implementerer vår første tilpassede type, la oss se en vanlig brukssak for implementering av en grunnleggende type. Anta at vi må jobbe med en eldre database som lagrer datoer som VARCHAR. Normalt, Dvalemodus vil kartlegge dette til String Java-type. Dermed blir datavalidering vanskeligere for applikasjonsutviklere.

Så la oss implementere vår LocalDateString type, som lagrer LocalDate Java-type som VARCHAR:

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

Det viktigste i denne koden er konstruktorparametrene. For det første er det en forekomst av SqlTypeDescriptor, som er Hibernates representasjon av SQL-typen, som er VARCHAR for vårt eksempel. Og det andre argumentet er en forekomst av JavaTypeDescriptor som representerer Java-typen.

Nå kan vi implementere en LocalDateStringJavaDescriptor for lagring og henting LocalDate som VARCHAR:

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

Deretter må vi overstyre pakke inn og pakke ut metoder for å konvertere Java-typen til SQL. La oss starte med pakke ut:

@ Override public X pakke ut (LocalDate-verdi, Klassetype, WrapperOptions-alternativer) {if (value == null) return null; hvis (String.class.isAssignableFrom (type)) return (X) LocalDateType.FORMATTER.format (verdi); kaste ukjent Avpakning (type); }

Neste, den pakke inn metode:

@ Override public LocalDate wrap (X value, WrapperOptions options) {if (value == null) return null; hvis (String.class.isInstance (verdi)) returnerer LocalDate.from (LocalDateType.FORMATTER.parse ((CharSequence) verdi)); kaste unknownWrap (value.getClass ()); }

pakke ut () kalles under PreparedStatement bindende å konvertere LocalDate til en strengtype, som er kartlagt til VARCHAR. Like måte, pakke inn() kalles under ResultatSett henting for å konvertere String til en Java LocalDate.

Til slutt kan vi bruke vår tilpassede type i Entity-klassen:

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

Senere får vi se hvordan vi kan registrere denne typen i dvale. Og som et resultat, referer til denne typen ved hjelp av registreringsnøkkelen i stedet for det fullt kvalifiserte klassenavnet.

4.2. Implementering Brukertype

Med mangfoldet av basistyper i dvalemodus er det veldig sjelden at vi trenger å implementere en tilpasset basistype. I motsetning er en mer typisk brukssak å kartlegge et komplekst Java-domeneobjekt til databasen. Slike domeneobjekter lagres vanligvis i flere databasekolonner.

Så la oss implementere et kompleks Telefonnummer innvende ved å implementere Brukertype:

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 overstyrte sqlTypes metoden returnerer SQL-typene av felt, i samme rekkefølge som de er deklarert i vår Telefonnummer klasse. På samme måte, returnerte klasse metoden returnerer vår Telefonnummer Java-type.

Det eneste du må gjøre er å implementere metodene for å konvertere mellom Java-type og SQL-type, slik vi gjorde for vår BasicType.

Først, den 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 (navn [1]); int tall = rs.getInt (navn [2]); PhoneNumber medarbeiderNummer = nytt Telefonnummer (landskode, bykode, nummer); retur ansattNummer; }

Neste, den nullSafeSet metode:

@Override public void nullSafeSet (PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) kaster HibernateException, SQLException {if (Objects.isNull (value)) {st.setNull (index, Types.INTEGER); st.setNull (indeks + 1, Typer.INTEGER); st.setNull (indeks + 2, Typer.INTEGER); } annet {PhoneNumber employeeNumber = (PhoneNumber) verdi; st.setInt (indeks, ansatteNummer.getCountryCode ()); st.setInt (indeks + 1, ansatteNummer.getCityCode ()); st.setInt (indeks + 2, ansatteNummer.getNumber ()); }}

Endelig kan vi erklære vår skikk PhoneNumberType i vår Kontor Ansatt enhetsklasse:

@Entity @Table (name = "OfficeEmployee") offentlig klasse OfficeEmployee {@Columns (kolonner = {@Column (name = "country_code"), @Column (name = "city_code"), @Column (name = "number") }) @Type (type = "com.baeldung.hibernate.customtypes.PhoneNumberType") private PhoneNumber ansatteNummer; // andre felt og metoder}

4.3. Implementering CompositeUserType

Implementering Brukertype fungerer bra for enkle typer. Kartlegging av komplekse Java-typer (med samlinger og kaskadede kompositttyper) trenger imidlertid mer sofistikering. Dvalemodus lar oss kartlegge slike typer ved å implementere CompositeUserType grensesnitt.

Så la oss se dette i aksjon ved å implementere en Adressetype for Kontor Ansatt enhet vi brukte tidligere:

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

I strid med Brukertyper, som kartlegger indeksen for typegenskapene, CompositeType kart eiendomsnavn på våre Adresse klasse. Enda viktigere, getPropertyType metoden returnerer kartleggingstypene for hver eiendom.

I tillegg må vi også implementere getPropertyValue og setPropertyValue metoder for kartlegging PreparedStatement og ResultatSett indekser for å skrive eiendom. Tenk som eksempel getPropertyValue for vår Adressetype:

@Override public Object getPropertyValue (Object component, int property) kaster HibernateException {Address empAdd = (Address) component; switch (property) {case 0: return empAdd.getAddressLine1 (); sak 1: returner empAdd.getAddressLine2 (); tilfelle 2: return empAdd.getCity (); tilfelle 3: returner empAdd.getCountry (); tilfelle 4: returner Integer.valueOf (empAdd.getZipCode ()); } kast ny IllegalArgumentException (eiendom + "er en ugyldig eiendomsindeks for klassetype" + component.getClass (). getName ()); }

Til slutt vil vi trenge å implementere nullSafeGet og nullSafeSet metoder for konvertering mellom Java- og SQL-typer. Dette ligner på det vi gjorde tidligere i vår PhoneNumberType.

Vær oppmerksom på at CompositeTypeEr generelt implementert som en alternativ kartleggingsmekanisme til Innbyggbar typer.

4.4. Type Parameterization

Foruten å lage tilpassede typer, Dvalemodus lar oss også endre atferden til typene basert på parametere.

Anta for eksempel at vi trenger å lagre Lønn for vår Kontor Ansatt. Enda viktigere, søknaden må konvertere lønnsbeløpettil geografisk lokal valuta beløp.

Så la oss implementere vår parameteriserte Lønnstype som godtar valuta som parameter:

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

Vær oppmerksom på at vi har hoppet over CompositeUserType metoder fra vårt eksempel for å fokusere på parameterisering. Her implementerte vi bare Hibernate DynamicParameterizedType, og overstyre setParameterValues ​​() metode. Nå, den Lønnstype godta a valuta parameter og konverterer hvilket som helst beløp før det lagres.

Vi passerer valuta som parameter mens du erklærer Lønn:

@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øp"), @Kolonne (navn = "valuta")}) privat Lønnslønn; // andre felt og metoder}

5. Basisregister

Dvalemodus opprettholder kartleggingen av alle innebygde basistyper i BasicTypeRegistry. Dermed elimineres behovet for å kommentere kartinformasjon for slike typer.

I tillegg lar dvalemodus oss registrere egendefinerte typer, akkurat som grunnleggende typer, i BasicTypeRegistry. Normalt vil applikasjoner registrere tilpasset type mens bootstrapping av SessionFactory. La oss forstå dette ved å registrere LocalDateString typen vi implementerte 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 egenskaper getProperties () {// returner dvalemodusegenskaper}

Og dermed, det tar bort begrensningen ved å bruke det fullt kvalifiserte klassenavnet i typekartlegging:

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

Her, LocalDateString er nøkkelen som LocalDateStringType er kartlagt.

Alternativt kan vi hoppe over Type registrering ved å definere TypeDefs:

@TypeDef (name = "PhoneNumber", typeClass = PhoneNumberType.class, defaultForType = PhoneNumber.class) @Entity @Table (name = "OfficeMedarbeider") offentlig klasse OfficeMedarbeider {@Kolonner (kolonner = {@Kolonne (navn = "landskode") ), @Column (name = "city_code"), @Column (name = "number")}) private PhoneNumber medarbeiderNummer; // andre metoder}

6. Konklusjon

I denne opplæringen diskuterte vi flere tilnærminger for å definere en tilpasset type i dvalemodus. I tillegg Vi implementerte noen få tilpassede typer for vår enhetsklasse basert på noen vanlige bruksområder der en ny tilpasset type kan komme til nytte.

Som alltid er kodeeksemplene tilgjengelig på GitHub.


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