Hibernate對一些基本類(lèi)型的映射提供了很好的支持,但有時(shí)候我們需要映射自定義或更復雜的數據類(lèi)型,比如一個(gè)List集合,可以通過(guò)基本類(lèi)型映射實(shí)現,需要在數據庫中新建一張表,這種方式增加了數據庫開(kāi)銷(xiāo);也可以將List集合中的數據拼接成字符串再存儲,這種方式導致程序可讀性不友好,同時(shí)增加代碼的復雜度;Hibernate提供了DiscriminatorType和UserType接口,方便用戶(hù)自定義需要映射的數據類(lèi)型。這里以UserType為例實(shí)現。
假設用戶(hù)需要自定義一個(gè)類(lèi)型,名稱(chēng)為ctype,它是16位字符的char數組,需要映射到數據庫VARCHAR類(lèi)型。
首先自定義 java 類(lèi)型ctype:
public class ctype implements Serializable { // mtype 的長(cháng)度 private static int TYPE_LENGTH = 16; // mtype 實(shí)際上就是 char 數組 private char[] mtype = new char[TYPE_LENGTH]; // 默認構造函數 public ctype() { this.mtype = new char[0]; } // 通過(guò)構造函數轉換為ctype類(lèi)型 public ctype(Object object) { String str = String.valueOf(object); if (StrUtil.isBlank(str)) this.mtype = new char[0]; this.mtype = str.toCharArray(); } public boolean isEmpty() { return String.valueOf(this.mtype).replace(" ", "").length() < TYPE_LENGTH; } public String toString() { return String.valueOf(this.mtype); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ctype ctype = (ctype) o; return Arrays.equals(mtype, ctype.mtype); } @Override public int hashCode() { return Arrays.hashCode(mtype); }}接下來(lái)實(shí)現Hibernate UserType接口:
/** * Hibernate自定義類(lèi)型 */public class customType implements UserType { private static int[] TYPES = new int[]{Types.VARCHAR}; /** * 修改類(lèi)型對應的SQL類(lèi)型(這里用VARCHAR */ @Override public int[] sqlTypes() { return TYPES; } /** * 修改customType對應的java類(lèi)型(此處java類(lèi)型為ctype */ @Override public Class returnedClass() { return ctype.class; } /** * 自定義數據類(lèi)型比對方法 * 用作臟數據檢查,o,o1為兩個(gè)副本 */ @Override public boolean equals(Object o, Object o1) throws HibernateException { return Objects.equals(o, o1); } /** * 返回給定類(lèi)型的hashCode */ @Override public int hashCode(Object o) throws HibernateException { return Objects.hashCode(o); } /** * 讀取數據轉換為自定義類(lèi)型返回 * strings包含了自定義類(lèi)型的映射字段名稱(chēng) */ @Override public Object nullSafeGet(ResultSet resultSet, String[] strings, SessionImplementor sessionImplementor, Object o) throws HibernateException, SQLException { String s = (String) resultSet.getObject(strings[0]); if (resultSet.wasNull()) return null; if (null != s) { if (s.replaceAll(" ", "").isEmpty()) return null; return new ctype(s); } return null; } /** * 數據保存時(shí)被調用 */ @Override public void nullSafeSet(PreparedStatement preparedStatement, Object o, int i, SessionImplementor sessionImplementor) throws HibernateException, SQLException { if (null == o) { preparedStatement.setNull(i, Types.VARCHAR); //保存空值 } else { String stringValue = String.valueOf(o); preparedStatement.setString(i, stringValue); } } /** * 自定義類(lèi)型的完全復制方法,構造返回對象 * 1. 當nullSafeGet方法調用之后,我們獲得了自定義數據對象, * 在向用戶(hù)返回自定義數據之前,deepCopy方法被調用, * 它將根據自定義數據對象構造一個(gè)完全拷貝,把拷貝返還給客戶(hù)使用。 * 2. 此時(shí)我們就得到了自定義數據對象的兩個(gè)版本 * 原始版本由hibernate維護,用作臟數據檢查依據; * 復制版本由用戶(hù)使用,hibernate將在臟數據檢查過(guò)程中比較這兩個(gè)版本的數據; */ @Override public Object deepCopy(Object o) throws HibernateException { if (o == null) return null; ctype c = (ctype) o; ctype nc = new ctype(); nc = c; return nc; } /** * 表示本類(lèi)型實(shí)例是否可變 */ @Override public boolean isMutable() { return true; } /** * method called when Hibernate puts the data in a second level cache. The * data is stored in a serializable form (官方文檔 */ @Override public Serializable disassemble(Object o) throws HibernateException { return (ctype) deepCopy(o); } /** * Returns the object from the 2 level cache (官方文檔 */ @Override public Object assemble(Serializable serializable, Object o) throws HibernateException{ return deepCopy(serializable); } @Override public Object replace(Object o, Object o1, Object o2) throws HibernateException { return deepCopy(o); }}通過(guò)注解方式使用:
/** * Entity -> Item */@Entity@Table(name = "d_item")@TypeDef(name = "myType", typeClass = customType.class)public class Item implements Serializable { @Id @Type(type = "myType") private ctype uid; ... ...}完成。
聯(lián)系客服