Egyéni típusok hibernált állapotban és a @Type kommentár

1. Áttekintés

A hibernálás egyszerűsíti az SQL és a JDBC közötti adatkezelést azáltal, hogy leképezi a Java objektumorientált modelljét az adatbázisok relációs modelljével. Habár az alapvető Java osztályok leképezése a Hibernate-ben van beépítve, az egyedi típusok leképezése gyakran bonyolult.

Ebben az oktatóanyagban megnézzük, hogy a Hibernate hogyan teszi lehetővé az alaptípus-hozzárendelés kiterjesztését az egyedi Java osztályokra. Emellett néhány egyedi példát is láthatunk az egyedi típusokról, és azokat a Hibernate típus-hozzárendelési mechanizmusával valósíthatjuk meg.

2. Hibernált térképészeti típusok

A hibernálás leképezési típusokat használ a Java objektumok konvertálásához SQL lekérdezésekké az adatok tárolásához. Hasonlóképpen, leképezési típusokat használ az SQL ResultSet Java objektumokká történő konvertálásához, miközben adatokat keres.

A hibernálás általában a típusokat entitás típusokba és értéktípusokba sorolja. Pontosabban, az entitástípusokat a tartományspecifikus Java-entitások feltérképezésére használják, és ezért az alkalmazás más típusaitól függetlenül léteznek. Ezzel szemben az Értéktípusok az objektumok feltérképezésére szolgálnak, és szinte mindig az Entitások tulajdonában vannak.

Ebben az oktatóanyagban az értéktípusok feltérképezésére fogunk összpontosítani, amelyeket tovább osztályozunk:

  • Alaptípusok - Alapvető Java típusok leképezése
  • Beágyazható - összetett java típusok / POJO-k leképezése
  • Gyűjtemények - Az alapszintű és az összetett java típus gyűjteményének leképezése

3. Maven-függőségek

Az egyedi hibernált típusok létrehozásához szükségünk lesz a hibernált magfüggőségre:

 org.hibernate hibernate-core 5.3.6.Végső 

4. Egyéni típusok hibernált állapotban

A legtöbb felhasználói tartományhoz használhatunk hibernált alaptérkép-típusokat. Számos olyan eset fordul elő azonban, amikor egyedi típust kell megvalósítanunk.

A hibernálás viszonylag megkönnyíti az egyedi típusok megvalósítását. Három megközelítés létezik az egyedi típus hibernált állapotban történő megvalósításához. Beszéljük meg részletesen mindegyiket.

4.1. Végrehajtás BasicType

A Hibernate-ek megvalósításával létrehozhatunk egy egyedi alaptípust BasicType vagy annak egyik konkrét megvalósítása, AbstractSingleColumnStandardBasicType.

Mielőtt az első egyedi típusunkat megvalósítanánk, nézzük meg az alaptípus megvalósításának általános használatát. Tegyük fel, hogy egy régi adatbázissal kell dolgoznunk, amely a dátumokat VARCHAR néven tárolja. Normális esetben, Hibernate ezt feltérképezné Húr Java típus. Ez megnehezíti a dátum érvényesítését az alkalmazásfejlesztők számára.

Tehát valósítsuk meg a mi LocalDateString típus, amely tárolja LocalDate Java típus VARCHAR néven:

public class LocalDateStringType kiterjeszti az AbstractSingleColumnStandardBasicType {public static final LocalDateStringType INSTANCE = new LocalDateStringType (); public LocalDateStringType () {super (VarcharTypeDescriptor.INSTANCE, LocalDateStringJavaDescriptor.INSTANCE); } @Orride public String getName () {return "LocalDateString"; }}

A legfontosabb ebben a kódban a konstruktor paraméterei. Először is a SqlTypeDescriptor, amely a Hibernate SQL típusú reprezentációja, amely példánkban VARCHAR. És a második érv a JavaTypeDescriptor amely a Java-típust képviseli.

Most megtehetjük megvalósítani a LocalDateStringJavaDescriptor tároláshoz és visszakereséshez LocalDate mint VARCHAR:

public class LocalDateStringJavaDescriptor kiterjeszti az AbstractTypeDescriptor {public static final LocalDateStringJavaDescriptor INSTANCE = új LocalDateStringJavaDescriptor (); public LocalDateStringJavaDescriptor () {super (LocalDate.class, ImmutableMutabilityPlan.INSTANCE); } // egyéb módszerek}

Ezután felül kell írnunk betakar és kibont módszerek a Java típus SQL-vé konvertálására. Kezdjük a kibont:

@Orride public X unfrap (LocalDate value, Class type, WrapperOptions options) {if (value == null) return null; if (String.class.isAssignableFrom (type)) return (X) LocalDateType.FORMATTER.format (érték); dobás ismeretlenUnwrap (típus); }

Ezután a betakar módszer:

@Orride public LocalDate wrap (X value, WrapperOptions options) {if (value == null) return null; if (String.osztály.isInstance (érték)) visszatér a LocalDate.from (LocalDateType.FORMATTER.parse ((CharSequence) érték)); dobja unknownWrap (value.getClass ()); }

kibont() alatt hívják PreparedStatement megkötés a megtéréshez LocalDate egy String típusra, amely a VARCHAR-hoz van hozzárendelve. Hasonlóképpen, betakar() alatt hívják ResultSet visszakeresés átalakításra Húr Java-ra LocalDate.

Végül használhatjuk egyéni típusunkat az Entity osztályunkban:

@Entity @Table (name = "OfficeEmployee") nyilvános osztály OfficeEmployee {@Column @Type (type = "com.baeldung.hibernate.customtypes.LocalDateStringType") private LocalDate dateOfJoining; // egyéb mezők és módszerek}

Később meglátjuk, hogyan regisztrálhatjuk ezt a típust a hibernálásban. És ennek eredményeként, erre a típusra a teljes minősítésű osztálynév helyett a regisztrációs kulcsot használja.

4.2. Végrehajtás UserType

A hibernált alaptípusok sokfélesége miatt nagyon ritkán kell egyedi alaptípust bevezetnünk. Ezzel szemben egy tipikusabb felhasználási eset egy összetett Java tartományi objektum leképezése az adatbázisba. Az ilyen tartományi objektumokat általában több adatbázis oszlopban tárolják.

Tehát valósítsunk meg egy komplexet Telefonszám objektum megvalósításával UserType:

public class PhoneNumberType implementálja a UserType {@Orride public int [] sqlTypes () {return new int [] {Types.INTEGER, Types.INTEGER, Types.INTEGER}; } @Orride public class ReturnClass () {return PhoneNumber.class; } // egyéb módszerek} 

Itt, felülbírálva sqlTypes A metódus a mezők SQL típusait adja vissza, a mi mezőnkben deklarált sorrendben Telefonszám osztály. Hasonlóképpen, returnClass metódus adja vissza a Telefonszám Java típus.

Csak annyit kell tennie, hogy implementálja a Java és az SQL típus közötti konverzió módszereit, ahogyan azt mi is tettük BasicType.

Először is a nullSafeGet módszer:

@Orride public Object nullSafeGet (ResultSet rs, String [] nevek, SharedSessionContractImplementor munkamenet, Object tulajdonos) dobja a HibernateException, SQLException {int countryCode = rs.getInt (nevek [0]); if (rs.wasNull ()) return null; int városkód = rs.getInt (nevek [1]); int szám = rs.getInt (nevek [2]); PhoneNumber workerNumber = új Telefonszám (countryCode, cityCode, number); visszatérő alkalmazottNumber; }

Ezután a nullSafeSet módszer:

@Override public void nullSafeSet (PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) HibernateException, SQLException {if (Objects.isNull (value)) {st.setNull (index, Types.INTEGER); st.setNull (index + 1, Types.INTEGER); st.setNull (index + 2, Types.INTEGER); } else {Telefonszám alkalmazottszám = (Telefonszám) érték; st.setInt (index, workerNumber.getCountryCode ()); st.setInt (index + 1, workerNumber.getCityCode ()); st.setInt (index + 2, workerNumber.getNumber ()); }}

Végül kijelenthetjük szokásunkat PhoneNumberType miénkben OfficeEmployee entitásosztály:

@Entity @Table (name = "OfficeEmployee") nyilvános osztály OfficeEmployee {@Columns (oszlopok = {@Column (név = "country_code"), @Column (name = "city_code"), @Column (name = "szám") }) @Type (type = "com.baeldung.hibernate.customtypes.PhoneNumberType") privát Telefonszám workerNumber; // egyéb mezők és módszerek}

4.3. Végrehajtás CompositeUserType

Végrehajtás UserType jól használható egyszerű típusoknál. Az összetett Java típusok leképezése (gyűjteményekkel és kaszkádos összetett típusokkal) azonban kifinomultabb. A hibernálás lehetővé teszi számunkra az ilyen típusok feltérképezését a CompositeUserType felület.

Tehát lássuk ezt működésben egy Cím típusa a OfficeEmployee korábban használt entitás:

public class AddressType implementálja a CompositeUserType {@Orride public String [] getPropertyNames () {return new String [] {"addressLine1", "addressLine2", "city", "country", "zipcode"}; } @Orride public Type [] getPropertyTypes () {return new Type [] {StringType.INSTANCE, StringType.INSTANCE, StringType.INSTANCE, StringType.INSTANCE, StringType.INSTANCE, IntegerType.INSTANCE}; } // egyéb módszerek}

Ellentétes UserTypes, amely feltérképezi a típus tulajdonságainak indexét, CompositeType feltérképezi a mi tulajdonneveinket Cím osztály. Ami még fontosabb, a getPropertyType metódus adja vissza az egyes tulajdonságok leképezési típusait.

Ezenkívül meg kell valósítanunk getPropertyValue és setPropertyValue térképészeti módszerek PreparedStatement és ResultSet indexel a tulajdonság beírásához. Példaként vegye fontolóra getPropertyValue a mi Cím típusa:

@ A nyilvános objektum felülírása a getPropertyValue (Objektumkomponens, int tulajdonság) dobja a HibernateException {Cím empAdd = (Cím) komponenst; kapcsoló (tulajdonság) {eset 0: return empAdd.getAddressLine1 (); 1. eset: return empAdd.getAddressLine2 (); 2. eset: return empAdd.getCity (); 3. eset: return empAdd.getCountry (); 4. eset: return Integer.valueOf (empAdd.getZipCode ()); } dob új IllegalArgumentException (a tulajdonság + "érvénytelen tulajdonságindex a" + type.getClass (). getName () osztálytípushoz; }

Végül meg kellene valósítanunk nullSafeGet és nullSafeSet módszerek a Java és az SQL típusok közötti átalakításra. Ez hasonlít ahhoz, amit korábban tettünk PhoneNumberType.

Kérjük, vegye figyelembe, hogy CompositeTypeA rendszer általában alternatív feltérképezési mechanizmusként valósul meg Beágyazható típusok.

4.4. Típus Paraméterezés

Az egyedi típusok létrehozása mellett A hibernálás lehetővé teszi számunkra a típusok viselkedésének megváltoztatását a paraméterek alapján.

Tegyük fel például, hogy tárolnunk kell a Fizetés a mi OfficeEmployee. Ennél is fontosabb, hogy a kérelemnek át kell alakítania a fizetés összegétföldrajzi helyi pénznemben.

Tehát hajtsuk végre a paraméterezettünket SalaryType amely elfogadja valuta paraméterként:

public class SalaryType megvalósítja a CompositeUserType, DynamicParameterizedType {private String localCurrency; @Orride public void setParameterValues ​​(Properties parameters) {this.localCurrency = parameters.getProperty ("valuta"); } // egyéb módszer implementációk a CompositeUserType-től}

Felhívjuk figyelmét, hogy kihagytuk a CompositeUserType módszereket a példánkból a paraméterezésre összpontosítani. Itt egyszerűen megvalósítottuk a Hibernate-eket DynamicParameterizedType, és felülírja a setParameterValues ​​() módszer. Most a SalaryType fogadja el a valuta paramétert, és bármilyen összeget konvertál a tárolása előtt.

Át fogjuk adni a valuta paraméterként, miközben a Fizetés:

@Entity @Table (name = "OfficeEmployee") public class OfficeEmployee {@Type (type = "com.baeldung.hibernate.customtypes.SalaryType", parameters = {@Parameter (name = "currency", value = "USD") }) @Columns (oszlopok = {@Column (név = "összeg"), @Column (név = "valuta")}) privát Fizetésbér; // egyéb mezők és módszerek}

5. Alaptípus-nyilvántartás

A Hibernate fenntartja az összes beépített alaptípus feltérképezését a BasicTypeRegistry. Így kiküszöbölve az ilyen típusú térképészeti információk feljegyzésének szükségességét.

Ezenkívül a hibernálás lehetővé teszi számunkra, hogy az alaptípusokhoz hasonlóan egyedi típusokat regisztráljunk a BasicTypeRegistry. Normális esetben az alkalmazások regisztrálják az egyedi típust, miközben a SessionFactory. Értsük meg ezt a LocalDateString korábban bevezetett típus:

privát statikus SessionFactory makeSessionFactory () {ServiceRegistry serviceRegistry = StandardServiceRegistryBuilder () .applySettings (getProperties ()). build (); MetadataSources metadataSources = új MetadataSources (serviceRegistry); Metadata metadata = metadataSources.getMetadataBuilder () .applyBasicType (LocalDateStringType.INSTANCE) .build (); return metadata.getSessionFactoryBuilder (). build ()} privát statikus tulajdonságok getProperties () {// hibernált tulajdonságok visszaadása}

Így, megszünteti a teljesen minősített osztálynév használatának korlátozását a Típus leképezésben:

@Entity @Table (name = "OfficeEmployee") nyilvános osztály OfficeEmployee {@Column @Type (type = "LocalDateString") private LocalDate dateOfJoining; // egyéb módszerek}

Itt, LocalDateString az a kulcs, amelyhez a LocalDateStringType feltérképezve van.

Alternatív megoldásként a típusregisztrációt definiálhatjuk TypeDefs:

@TypeDef (name = "PhoneNumber", typeClass = PhoneNumberType.class, defaultForType = PhoneNumber.class) @Entity @Table (name = "OfficeEmployee") public class OfficeEmployee {@Columns (columns = {@Column (name = "country_code") ), @Column (név = "város_kód"), @Column (név = "szám")}) privát Telefonszám workerNumber; // egyéb módszerek}

6. Következtetés

Ebben az oktatóanyagban többféle megközelítést is megvitattunk az egyedi típus definiálásához hibernált állapotban. Ezenkívül néhány egyedi típust vezettünk be az entitásosztályunkhoz néhány olyan általános felhasználási eset alapján, amikor egy új egyedi típus jól jöhet.

Mint mindig, a kódminták elérhetők a GitHubon.