现在的位置: 首页 > 综合 > 正文

hibernate自定义类型

2013年10月03日 ⁄ 综合 ⁄ 共 5030字 ⁄ 字号 评论关闭

开发者创建属于他们自己的值类型也是很容易的。比如说,你可能希望持久化java.lang.BigInteger类型的属性,持久化成为VARCHAR字段。Hibernate没有内置这样一种类型。自定义类型能够映射一个属性(或集合元素)到不止一个数据库表字段。比如说,你可能有这样的Java属性:getName()/setName(),这是java.lang.String类型的,对应的持久化到三个字段:FIRST_NAME, INITIAL, SURNAME

 

 

 

 

 

 

    <column name="first_string"/>

    <column name="second_string"/>

</property>

注意使用<column>标签来把一个属性映射到多个字段的做法。

 

 

 

 

 

 

 

 

 

    <type name="com.mycompany.usertypes.DefaultValueIntegerType">

        <param name="default">0</param>

    </type>

</property>

现在,UserType 可以从传入的Properties对象中得到default 参数的值。

 

 

 

 

 

 

    <param name="default">0</param>

</typedef>

<property name="priority" type="default_zero"/>

也可以根据具体案例通过属性映射中的类型参数覆盖在typedef中提供的参数。

 

尽管 Hibernate 内建的丰富的类型和对组件的支持意味着你可能很少 需要使用自定义类型。不过,为那些在你的应用中经常出现的(非实体)类使用自定义类型也是一个好方法。例如,一个MonetaryAmount类使用CompositeUserType来映射是不错的选择,虽然他可以很容易地被映射成组件。这样做的动机之一是抽象。使用自定义类型,以后假若你改变表示金额的方法时,它可以保证映射文件不需要修改。

--------------------------------------------------------------------------

 Hibernate 提供了客户化映射类型接口,允许用户以编程的方式创建自定义的映射类型,以便把持久化类的任意类型的属性映射到数据库中.例1的PhoneUserType实现了net.sf.hibernate.UserType接口,它能够把Customer类的Integer类型的phone属性映射到CUSTOMER表的VARCHAR类型的PHONE字段.

例1:

package mypack;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import net.sf.hibernate.Hibernate;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.UserType;

/**
 * @author lfm
 *
 */
public class PhoneUserType implements UserType {
 
 private static final int[] SQL_TYPES = {Types.VARCHAR};
 
 public int[] sqlTypes() {
  // TODO 自动生成方法存根
  return SQL_TYPES;
 }
 
 public Class returnedClass() {
  // TODO 自动生成方法存根
  return Integer.class;
 }
 
 public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
  // TODO 自动生成方法存根
  if(resultSet.wasNull())
   return null;
  String phone = resultSet.getString(names[0]);
  return new Integer(phone);
 }
 
 public void nullSafeSet(PreparedStatement statement, Object value, int index) throws HibernateException, SQLException {
  // TODO 自动生成方法存根
  if(value== null)
   arg0.setNull(index, Types.VARCHAR);
  else {
   String phone = ((Integer)value).toString();
   statement.setString(index, phone);
  }
 }
 
 public Object deepCopy(Object value) throws HibernateException {
  // TODO 自动生成方法存根
  return value;
 }
 
 public boolean equals(Object x, Object y) throws HibernateException {
  // TODO 自动生成方法存根
  if(x== y)
   return true;
  if(x== null || y== null)
   return false;
  return x.equals(y);
 }
 
 public boolean isMutable() {
  // TODO 自动生成方法存根
  return false;
 }
}

PhoneUserType实现了net.sf.hibernate.UserType接口中的所有方法,下面解释这些方法的作用.

(1)设置CUSTOMERS表的PHONE字段SQL类型,它是VARCHAR类型:

private static final int[] SQL_TYPES = {Types.VARCHAR};

public int[] sqlTypes() {return SQL_TYPES;}

(2)设置Customer类的phone属性的Java类型,它是Integer类型:

public CLass returnedClass() {return Integer.class;}

(3)Hibernate调用isMutable()方法来了解Integer类是否是可变类.本例的Integer类是不可变类,因此返回false.Hibernate处理不可变属性类型的属性时,会采取一些性能优化措施.

public boolean isMutable() {return false;}

(4)Hibernate调用deepCopy(Object value)方法来生成phone属性的快照.deepCopy()方法的value参数代表Integer类型的phone属性.由于Integer类是不可变类,因此本方法直接返回参数:

public Object deepCopy(Object value) {

  return value;

}

对于可变类,必须返回参数的拷贝值.后面会说.

(5)Hibernate调用equals(Object x, Object y)方法类比较Customer类的phone属性的当前值是否和它的快照相同.该方法的一个参数代表phone属性的当前值,一个参数代表由deepCopy()方法生成的phone属性的快照:

public boolean equals(Object x, Object y) {

  if(x== y)

    return true;

  if(x== null || y== null)

    return false;

  return x.equals(y);

}

(6)当Hibernate从数据库加载Customer对象时,调用nullSafeGet()方法类取得phone属性值.参数resultSet为JDBC查询结果集,参数names为存放了表字段名的数组,此处为{"PHONE"},参数owner代表phone属性的宿主,此处为Customer对象.

public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException,SQLException {

  if(resultSet.wasNull()) return null;

  String phone = resultSet.getString(names[0]);

  return new Integer(phone);

}

在nullSafeGet方法中,先从ResultSet从读取PHONE字段的值,然后把它转换为Integer对象,最后将它作为phone属性值返回.

(7)当Hibernate把Customer对象持久化到数据库中时,调用nullSafeSet()方法来把phone属性添加到INSERT SQL语句中.statement参数包含了预定义的INSERT SQL语句,参数value代表phone属性,参数index代表把phone属性插入到INSERT SQL语句中的位置:

public void nullSafeSet(PreparedStatement statement, Object value, int index) throws HibernateException, SQLException {

  if(value == null) {

    statement.setNull(index, Types.VARCHAR);

  }else{

    String phone = ((Integer)value).toString();

    statement.setString(index, phone);

  }

}

在nullSafeSet()方法中,参数value代表phone属性.因此,先把Integer类型的value转换为String类型,然后把它添加到JDBC Statement中.

定义了PhoneUserType类后,在Customer.hbm.xml中按如下方式映射Customer类的phone属性:

<property name="name" type="mypack.PhoneUserType">

  <column name="phone" length="8"/>

</property>

PhoneUserType不仅仅可以用来映射phone属性,事实上,它能够把持久化类的任何一个Integer类型的属性映射到数据库中VARCHAR类型的字段.

<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">

如果你非常频繁地使用某一UserType,可以为他定义一个简称。这可以通过使用 <typedef>元素来实现。Typedefs为一自定义类型赋予一个名称,并且如果此类型是参数化的,还可以包含一系列默认的参数值。

<property name="priority">

你甚至可以在一个映射文件中提供参数给一个UserType 为了这样做,你的UserType必须实现org.hibernate.usertype.ParameterizedType接口。为了给自定义类型提供参数,你可以在映射文件中使用<type>元素。

CompositeUserType, EnhancedUserType, UserCollectionType, UserVersionType 接口为更特殊的使用方式提供支持。

<property name="twoStrings" type="org.hibernate.test.DoubleStringType">

要实现一个自定义类型,可以实现org.hibernate.UserTypeorg.hibernate.CompositeUserType中的任一个,并且使用类型的Java全限定类名来定义属性。请查看org.hibernate.test.DoubleStringType这个例子,看看它是怎么做的。

抱歉!评论已关闭.