精通hibernate学习笔记(6)[映射类型]

Hibernate映射类型分为两种:内置映射类型和客户化映射类型,内置映射类型负责把常见的java类型映射到相应的sql类型;另外,用户可以实现UserType或CompositeUserType接口,来定制客户化映射类型,这样可以把用户定义 的java类型映射到数据库表的相应字段。

1、内置映射类型

1.1 java基本类型的hibernate映射类型

1.2 java时间和日期类型的hibernate映射类型
精通hibernate学习笔记(6)[映射类型]
1.3 Java大对象类型的hibernate映射类型
精通hibernate学习笔记(6)[映射类型]
注意:不允许使用这些数据类型来定义持久化类的OID。
CLOB:字符串大对象(Character Large Object)
BLOB:表示二进制大对象(Binary Large Object)
MySql中不支持标准SQL的CLOB类型,MySQL中使用TEXT,MEDIUMTEXT,LONGTEXT类型类表示长度超过255的长文本数据,分别为0~65535,0~16777215,0~4294967295字节。

如果持久化类的某个字段为java.sql.Clob或java.sql.Blob实例时,映射设置如下:
<property name="description" type="clob" column="DESCRIPTION">

在应用程序中通过Hibernate来保存java.sql.Clob或java.sql.Blob实例时,必须包括以下两个步骤:
a. 在数据库事务中先保存一个空的Blob或Clob实例。
b. 接着锁定这条记录,更新在a中保存的实例,把二进制数据或长文本数据写到Blob或Clob实例中。
例:
Session session = sessionFactory.openSession();
Transaction tx = session.begainTransaction();
customer = new Customer();
//现保存空的clob实例
customer.setDescription(Hibernate.createClob(" "));
session.save(customer);
session.flush();
//锁定这条记录
session.refresh(customer,LockMode.UPGRADE);
oracle.sql.Clob clob = (Oracle.sql.Clob)customer.getDescription();
//把长文本数据写到Clob实例中
java.io.Writer.pw=clob.getCharacterOutputStream();
pw.write(longText);//longText表示一个长度〉255的字符串
pw.close();
tx.commit();
session.close();

尽管java.sql.Clob和java.sql.Blob是处理java大对象的有效方式,但是使用二者受到以下两点限制:
a. 如果在持久化类中定义了一个该类型的属性,只有在一个数据库事务中,这样的实例才会有效。
b. 有些数据库系统的jdbc驱动程序不支持这样的类型。

如果在java程序中处理图片或长文件的二进制数据,使用byte[]比Blob更方便,处理长度>255的字符串使用String比Clob更方便。

1.4 JDK自带的个别java类的hibernate映射类型

1.5 使用映射类型

在以下情况下必须显示指定映射文件中的hibernate映射类型:
a. 如果希望通过hbm2java工具由映射文件来生成持久化类,必须在映射文件中显式指定映射类型
b. 一个java类型对应多个Hibernate映射类型的场合。如:java.util.Date,可以对应Hibernate的映射类型中的date,time,timestamp。

2、客户化映射类型

2.1 用户化映射类型取代Hibernate组件

曾经用组件来映射Customer类的Address类型的homeAddress属性和comAddress属性,同样可以用自定义映射类型来实现。

把Address设计为不可变类,即当创建了这种类的实例以后,就不允许修改它的属性。java中所有基本类型的包装类都是不可变类:Integer,Long等。创建用户自己的不可变类时,可以考虑以下设计模式:
a. 把属性定义为private final类型。
b. 不对外公开set方法
c. 只对外公开get方法
d. 允许在构造方法中设置所有属性。
e. 覆盖Object类的equals和hashCode方法,在equals方法中根据对象的属性值来比较两个对象是否相等,保证用equals方法比较相等的两个对象的hashCode方法返回值也相等。
package mypack;
import java.io.Serializable;

public class Address implements Serializable ...{

    private final String province;
    private final String city;
    private final String street;
    private final String zipcode;

    public Address(String province, String city, String street, String zipcode) ...{
        this.street = street;
        this.city = city;
        this.province = province;
        this.zipcode = zipcode;

    }

    public String getProvince() ...{
        return this.province;
    }
    public String getCity() ...{
        return this.city;
    }

    public String getStreet() ...{
        return this.street;
    }

    public String getZipcode() ...{
        return this.zipcode;
    }


    public boolean equals(Object o)...{
        if (this == o) return true;
        if (!(o instanceof Address)) return false;

     final Address address = (Address) o;

     if(!province.equals(address.province)) return false;
     if(!city.equals(address.city)) return false;
     if(!street.equals(address.street)) return false;
     if(!zipcode.equals(address.zipcode)) return false;
     return true;
    }
    public int hashCode()...{
        int result;
    result= (province==null?0:province.hashCode());
    result = 29 * result + (city==null?0:city.hashCode());
        result = 29 * result + (street==null?0:street.hashCode());
        result = 29 * result + (zipcode==null?0:zipcode.hashCode());
    return result;
    }
}
package mypack;

import net.sf.hibernate.*;
import java.sql.*;

public class AddressUserType implements UserType ...{

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

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

  public Class returnedClass() ...{ return Address.class; }

  public boolean isMutable() ...{ return false; }

  public Object deepCopy(Object value) ...{
    return value; // Address is immutable
  }

  public boolean equals(Object x, Object y) ...{
    if (x == y) return true;
    if (x == null || y == null) return false;
    return x.equals(y);
  }

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

    if (resultSet.wasNull()) return null;
    String province = resultSet.getString(names[0]);
    String city = resultSet.getString(names[1]);
    String street = resultSet.getString(names[2]);
    String zipcode = resultSet.getString(names[3]);
    return new Address(province,city,street,zipcode);
  }

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

    if (value == null) ...{
      statement.setNull(index, Types.VARCHAR);
      statement.setNull(index+1, Types.VARCHAR);
      statement.setNull(index+2, Types.VARCHAR);
      statement.setNull(index+3, Types.VARCHAR);
    } else ...{
      Address address=(Address)value;
      statement.setString(index, address.getProvince());
      statement.setString(index+1, address.getCity());
      statement.setString(index+2, address.getStreet());
      statement.setString(index+3, address.getZipcode());
    }
  }
}
    <property name="homeAddress" type="mypack.AddressUserType" >
        <column name="HOME_STREET" length="15" />
        <column name="HOME_CITY" length="15" />
        <column name="HOME_PROVINCE" length="15" />
        <column name="HOME_ZIPCODE" length="6" />
    </property>

    <property name="comAddress" type="mypack.AddressUserType" >
        <column name="COM_STREET" length="15" />
        <column name="COM_CITY" length="15" />
        <column name="COM_PROVINCE" length="15" />
        <column name="COM_ZIPCODE" length="6" />
    </property>

注:映射文件列的顺序要与自定义type中的列名对应顺序一致,如果不一致可能会取出的数据没错,但是库表中的数据列的位置不一致,同时可能出现类型不匹配。能取出一致数据的地原因是nullSafeSet和nullSafeGet中列的顺序一致!

精通hibernate学习笔记(6)[映射类型]