Hibernate識別數(shù)據(jù)庫特有字段實例詳解
Hibernate識別數(shù)據(jù)庫特有字段實例詳解
前言:
Hibernate已經(jīng)為絕大多數(shù)常用的數(shù)據(jù)庫數(shù)據(jù)類型提供了內(nèi)置支持,但對于某些數(shù)據(jù)庫的專屬字段支持就不夠好了。 這些特殊數(shù)據(jù)類型往往提供了比常規(guī)數(shù)據(jù)類型更好的數(shù)據(jù)表達能力,更符合我們的業(yè)務(wù)場景。比如PostgreSQL的Interval類型,可以非常方便的保存一個時間段的數(shù)據(jù)。 本文以添加Interval類型支持為例,說明為Hibernate添加特有數(shù)據(jù)類型支持的方法。
Hibernate提供了豐富的數(shù)據(jù)類型支持,但對于部分數(shù)據(jù)庫專有的數(shù)據(jù)類型,提供的支持就很有限了。比如PostgreSQL的Interval類型,對于保存一個"時間段"數(shù)據(jù)就非常方便。
在開發(fā)中,我們期望將Interval類型映射為Java 8 的Duration類型。但是Hibernate默認對Duration類型的映射是直接映射到數(shù)據(jù)庫的BigInt類型,直接保存納秒值。顯然對于不直接支持Interval類型的數(shù)據(jù)庫來說,是比較合適的,但是我們?nèi)匀黄谕苯佑成涞綌?shù)據(jù)庫的Interval類型。
為此,我們需要調(diào)整Hibernate對于兩種數(shù)據(jù)類型(Java世界的Duration和Db世界的Interval)的映射關(guān)系。
幸運的是,Hibernate提供了非常方便的方法可以實現(xiàn)數(shù)據(jù)類型的映射。
為此,我們需要一個實現(xiàn)org.hibernate.usertype.UserType接口的類,來實現(xiàn)兩個世界的數(shù)據(jù)轉(zhuǎn)換/映射工作。
Hibernate的自定義類型(UserType)
UserType是Hibernate提供的一個自定義數(shù)據(jù)類型的接口。所有自定義數(shù)據(jù)均需實現(xiàn)此接口,或者從org.hibernate.usertype中定義的接口中選擇一個合適的接口。
鑒于我們的場景比較簡單,直接實現(xiàn)UserType即可滿足需求。此接口提供了如下一組方法需要自己實現(xiàn):
assemble(Serializable cached, Object owner)
從序列化中重新構(gòu)建(Java)對象。
deepCopy(Object value)
返回深度副本。
disassemble(Object value)
轉(zhuǎn)換對象的序列化數(shù)據(jù)。
equals(Object x, Object y)
返回兩個映射的數(shù)據(jù)是否相等。
hashCode(Object x)
獲取對象的散列。
isMutable()
返回對象是否是可變類型。
nullSafeGet(ResultSet rs, String[] names, Object owner)
從數(shù)據(jù)庫類型的數(shù)據(jù),返回對應(yīng)的Java對象。核心實現(xiàn)方法
nullSafeSet(PreparedStatement st, Object value, int index)
從Java對象,返回對應(yīng)的數(shù)據(jù)庫類型的數(shù)據(jù)。核心實現(xiàn)方法
replace(Object original, Object target, Object owner)
合并期間,將實體中的目標(biāo)值(target)替換為原始值(original)。
returnedClass()
nullSafeGet返回的類。
sqlTypes()
返回對應(yīng)的數(shù)據(jù)庫類型。
實例
package framework.postgresql; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.usertype.UserType; import org.postgresql.util.PGInterval; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.time.Duration; /** * PostgreSql Inteval字段與java.time.Duration映射 * 目前只支持到最多1個月(30天)的間隔 * <p> * 使用方法: * 在實體類上增加 * \@TypeDef(name="interval", typeClass = IntervalType.class) * 在字段定義上增加: * \@Type(type = "interval") * <p> * http://stackoverflow.com/questions/1945615/how-to-map-the-type-interval-in-hibernate/6139581#6139581 * * @version 1.0 * @since 1.0 */ public class IntervalType implements UserType { public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } public Object deepCopy(Object value) throws HibernateException { return value; } public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } public boolean equals(Object arg0, Object arg1) throws HibernateException { return arg0 != null && arg1 != null && arg0.equals(arg1) || arg0 == null && arg1 == null; } public int hashCode(Object object) throws HibernateException { return object.hashCode(); } @Override public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor sessionImplementor, Object o) throws HibernateException, SQLException { String interval = resultSet.getString(names[0]); if (resultSet.wasNull() || interval == null) { return null; } PGInterval pgInterval = new PGInterval(interval); return getDuration(pgInterval); } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor sessionImplementor) throws HibernateException, SQLException { if (value == null) { st.setNull(index, Types.OTHER); } else { //this http://postgresql.1045698.n5.nabble.com/Inserting-Information-in-PostgreSQL-interval-td2175203.html#a2175205 Duration duration = (Duration) value; st.setObject(index, getInterval(duration), Types.OTHER); } } public static Duration getDuration(PGInterval pgInterval) { return Duration.ofSeconds(pgInterval.getDays() * 24 * 3600 + pgInterval.getHours() * 3600 + pgInterval.getMinutes() * 60 + (int) pgInterval.getSeconds()); } private static PGInterval getInterval(Duration value) { long seconds = value.getSeconds(); int days = (int) (seconds / (24 * 3600)); seconds -= days * 24 * 3600; int hours = (int) (seconds / 3600); seconds -= hours * 3600; int minutes = (int) (seconds / 60); seconds -= minutes * 60; seconds = Math.abs(seconds); return new PGInterval(0, 0, days, hours, minutes, seconds); } public boolean isMutable() { return false; } public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } public Class returnedClass() { return Duration.class; } public int[] sqlTypes() { return new int[]{Types.OTHER}; } }
使用自定義類型
至此,我們已經(jīng)定義好了自己的數(shù)據(jù)類型。但Hibernate還不知道怎么使用它。為此,我們需要通過在Entity上使用使用TypeDef注解,并在屬性上使用Type注解。
比如:
... @Entity @TypeDef(name = "interval", typeClass = IntervalType.class) public class PaperStatis implements Serializable { ... @Column(name = "avg_duration") @Type(type = "interval") public Duration getAvgDuration() { return this.avgDuration; } ... }
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
在JSP中訪問MS SQL Server數(shù)據(jù)庫
在JSP中訪問MS SQL Server數(shù)據(jù)庫...2006-10-10jsp簡單自定義標(biāo)簽的forEach遍歷及轉(zhuǎn)義字符示例
這篇文章主要介紹了jsp簡單自定義標(biāo)簽的forEach遍歷及轉(zhuǎn)義字符,需要的朋友可以參考下2014-03-03