程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> Hibernate中的自定義類型——UserType、CompositeUserType

Hibernate中的自定義類型——UserType、CompositeUserType

編輯:MySQL綜合教程

Hibernate中的自定義類型——UserType、CompositeUserType


一、UserType

Hibernate擁有自定義映射表屬性的機制,主要通過實現接口UserType,具體的UserType:

import java.sql.PreparedStatement;     
import java.sql.ResultSet;     
import java.sql.SQLException;     
     
import net.sf.hibernate.HibernateException;     
     
/**   
 * @author hy-he   
 *   
 */     
public interface UserType {     
     
 /**   
  * 返回UserType所映射字段的SQL類型(java.sql.Types)   
  * 返回類型為int[],其中包含了映射個字段的SQL類型代碼   
  * (UserType可以映射到一個或者多個字段)   
  * @return   
  */     
 public int[]sqlTypes();     
     
     
 /**   
  * UserType.nullSafeGet()所返回的自定義數據類型   
  * @return   
  */     
 public Class returnedClass();     
     
     
 /**   
  * 自定義數據類型的比對方法   
  * 此方法將用作髒數據檢查,參數x、y分別為數據的兩個副本   
  * 如果equals方法返回false,則Hibernate將認為數據發生變化,並將變化更新到數據庫表中   
  * @param x   
  * @param y   
  * @return   
  * @throws HibernateException   
  */     
 public boolean equals(Object x,Object y)throws HibernateException;     
     
     
 /**   
  * 從JDBC ResultSet讀取數據,將其轉換為自定義類型後返回   
  * (此方法要求對克能出現null值進行處理)   
  * names中包含了當前自定義類型的映射字段名稱   
  * @param rs   
  * @param names   
  * @param owner   
  * @return   
  * @throws HibernateException   
  * @throws SQLException   
  */     
 public Object nullSafeGet(ResultSet rs,String[] names,Object owner)throws HibernateException,SQLException;     
     
     
 /**   
  * 本方法將在Hibernate進行數據保存時被調用   
  * 我們可以通過PreparedStateme將自定義數據寫入到對應的數據庫表字段   
  * @param st   
  * @param value   
  * @param index   
  * @throws HibernateException   
  * @throws SQLException   
  */     
 public void nullSafeSet(PreparedStatement st,Object value,int index)throws HibernateException,SQLException;     
     
     
 /**   
  * 提供自定義類型的完全復制方法   
  * 本方法將用構造返回對象   
  * 當nullSafeGet方法調用之後,我們獲得了自定義數據對象,在向用戶返回自定義數據之前,   
  * deepCopy方法將被調用,它將根據自定義數據對象構造一個完全拷貝,並將此拷貝返回給用戶   
  * 此時我們就得到了自定義數據對象的兩個版本,第一個是從數據庫讀出的原始版本,其二是我們通過   
  * deepCopy方法構造的復制版本,原始的版本將有Hibernate維護,復制版由用戶使用。原始版本用作   
  * 稍後的髒數據檢查依據;Hibernate將在髒數據檢查過程中將兩個版本的數據進行對比(通過調用   
  * equals方法),如果數據發生了變化(equals方法返回false),則執行對應的持久化操作   
  *   
  * @param value   
  * @return   
  * @throws HibernateException   
  */     
 public Object deppCopy(Object value)throws HibernateException;     
     
     
 /**   
  * 本類型實例是否可變   
  * @return   
  */     
 public boolean isMutable();     
}     
例子:一個學生有聯系地址,而聯系地址又分為家庭地址和工作地址,我們可以把兩個地址信息抽象成一個新的Address類,作為Student的成員變量。

 

 

數據庫結構:

 

\create table typestu (id varchar(32) primary key,name varchar(32),homeaddr varchar(32),workaddr varchar(32));

Hibernate.cfg.xml

 

\
\ \ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
\ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
\
\
\
\
\
\ root
\
\ jdbc:mysql://localhost:3306/schoolproject?characterEncoding=gb2312&useUnicode=true
\

\
\ org.hibernate.dialect.MySQLDialect
\

\ mysql
\ 1234
\
\ com.mysql.jdbc.Driver
\

\
\ org.hibernate.dialect.MySQLDialect
\

\ true
\ thread
\
\
\

\
\

Pojo

 

\package Search.UserType;
\
\
\public class Student {
\ private String id; //標識id
\ private String name; //學生姓名
\ private AddressType address;//地址
\ public String getId() {
\ return id;
\ }
\ public void setId(String id) {
\ this.id = id;
\ }
\ public String getName() {
\ return name;
\ }
\ public void setName(String name) {
\ this.name = name;
\ }
\ public AddressType getAddress() {
\ return address;
\ }
\ public void setAddress(AddressType address) {
\ this.address = address;
\ }
\
\
\
\
\}
\

 

自定義類型

 

\
\package Search.UserType;
\
\import java.io.Serializable;
\import java.sql.PreparedStatement;
\import java.sql.ResultSet;
\import java.sql.SQLException;
\import java.sql.Types;
\
\import org.apache.commons.lang.builder.EqualsBuilder;
\import org.apache.commons.lang.builder.HashCodeBuilder;
\import org.hibernate.HibernateException;
\import org.hibernate.usertype.UserType;
\
\public class AddressType implements UserType, Serializable {
\ private String homeAddr;
\ private String workAddr;
\
\ /* 有幾個字段就有幾個值,這裡容易出錯,要多注意 */
\ private static final int[] SQL_TYPES = { Types.VARCHAR, Types.VARCHAR };
\
\ /* 這個方法告訴Hibernate在成生DDL時對列采用什麼樣的SQL語法 */
\ public int[] sqlTypes() {
\ return SQL_TYPES;
\ }
\
\ /*
\ * Hibernate返回什麼樣的映射類型,與
\ * 指定的類一致。事實上也可以把AddressType拆分為兩個類,一個類是只攜帶信息的JavaBean,它裡面
\ * 沒有邏輯操作也沒有實現UserType(比如AddressBean);而另一個類實現了UserType,它所面對的就不是現在這個
\ * AddressType類的homeAddr和homeAddr屬性,它面對的是AddressBean。在本例中為了簡潔方便,只用了一個類。
\ */
\ public Class returnedClass() {
\ return AddressType.class;
\ }
\
\ /*
\ * 表明這個類的實例在創建以後就不可以改變屬性。Hibernate能為不可改變的類作一些性能優化。
\ */
\ public boolean isMutable() {
\ return false;
\ }
\
\ /*
\ * 由於AddressType是不可變的,所以深拷貝可以直接返回對象引用。拷貝的對象由應用程序使用, 而原版對象由Hibernate維護以做髒數據檢查
\ */
\ public Object deepCopy(Object value) {
\ return value; // Address is immutable
\ }
\
\ /* 兩個對象是否相等,使用了apache的common工具包來進行屬性比對 */
\ public boolean equals(Object x, Object y) {
\ if (x == y)
\ return true;
\ if (x == null || y == null)
\ return false;
\ AddressType add1 = (AddressType) x;
\ AddressType add2 = (AddressType) y;
\ return new EqualsBuilder() //使用EqualsBuilder類來方便地進行比對
\ .append(add1.getHomeAddr(), add2.getHomeAddr()).append(
\ add2.getWorkAddr(), add2.getWorkAddr()).isEquals();
\ }
\
\ /* 得到hash碼 */
\
\ public int hashCode(Object x) throws HibernateException {
\ AddressType address = (AddressType) x;
\ return new HashCodeBuilder()//使用HashCodeBuilder類來方便地進行比對
\ .append(address.getHomeAddr()).append(address.getWorkAddr())
\ .toHashCode();
\ }
\
\ /* 讀取數據並組裝成一個AddressType對象。names[]中的參數順序依照映射文件中定義的順序 */
\ public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
\ throws HibernateException, SQLException {
\ if (rs.wasNull())
\ return null;
\ String homeAddr = rs.getString(names[0]);
\ String schoolAddr = rs.getString(names[1]);
\ AddressType address = new AddressType(homeAddr, schoolAddr);
\ return address;
\ }
\
\ /* 保存數據,index的順序按照映射文件定義的順序,從0開始。 */
\ public void nullSafeSet(PreparedStatement st, Object value, int index)
\ throws HibernateException, SQLException {
\ AddressType address = (AddressType) value;
\ if (value == null) {
\ st.setNull(index, Types.VARCHAR);
\ st.setNull(index+1, Types.VARCHAR);
\ } else {
\ st.setString(index, address.getHomeAddr());
\ st.setString(index + 1, address.getWorkAddr());
\ }
\ System.out.println("Data has been saved! ");
\ }
\
\ /* 當把AddressType類型數據寫入二級緩存時,此方法被調用 */
\ public Serializable disassemble(Object value) throws HibernateException {
\ return null;
\ }
\
\ /* 當從二級緩存中讀取AddressType類型數據時,此方法被調用 */
\ public Object assemble(Serializable cached, Object owner)
\ throws HibernateException {
\ // TODO 自動生成方法存根
\ return null;
\ }
\
\ public Object replace(Object original, Object target, Object owner)
\ throws HibernateException {
\ // TODO 自動生成方法存根
\ return null;
\ }
\
\ public AddressType() {
\ super();
\ }
\
\ public AddressType(String homeAddr, String workAddr) {
\ super();
\ this.homeAddr = homeAddr;
\ this.workAddr = workAddr;
\ }
\
\ /**
\ * @return 返回 sQL_TYPES。
\ */
\ public static int[] getSQL_TYPES() {
\ return SQL_TYPES;
\ }
\
\ /**
\ * @return 返回 homeAddr。
\ */
\ public String getHomeAddr() {
\ return homeAddr;
\ }
\
\ /**
\ * @param homeAddr
\ * 要設置的 homeAddr。
\ */
\ private void setHomeAddr(String homeAddr) {
\ this.homeAddr = homeAddr;
\ }
\
\ private String getWorkAddr() {
\ return workAddr;
\ }
\
\ private void setWorkAddr(String workAddr) {
\ this.workAddr = workAddr;
\ }
\
\
\
\
\
\}
\

 

Student.hbm.xml

 

\
\ \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
\
\
\
\
\
\

\
\
\
\
\

\

\

\
\

 

測試代碼:

 

\package Search.UserType;
\
\import java.io.File;
\import java.io.FileInputStream;
\import java.io.FileNotFoundException;
\import java.io.IOException;
\import java.sql.Blob;
\import java.sql.Clob;
\
\import org.hibernate.Hibernate;
\import org.hibernate.Session;
\import org.hibernate.SessionFactory;
\import org.hibernate.Transaction;
\import org.hibernate.cfg.Configuration;
\
\public class Test {
\
\
\ public static void main(String[] args) {
\ String filePath=System.getProperty("user.dir")+File.separator+"src/Search/UserType"+File.separator+"hibernate.cfg.xml";
\ File file=new File(filePath);
\ SessionFactory sessionFactory=new Configuration().configure(file).buildSessionFactory();
\ Session session=sessionFactory.openSession();
\ Transaction tx=session.beginTransaction();
\ AddressType address=new AddressType("home","work");
\ Student stu=new Student();
\ stu.setName("tome1");
\ stu.setAddress(address);
\ session.save(stu);
\ tx.commit();
\
\
\
\ }
\
\}
\ 二、CompositeUserType

CompositeUserType擁有UserType所有的接口,並提供了更加豐富的功能,可以實現更加復雜的功能。

待續...
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved