在hibernate中持久化枚举类型

 
前言
    在JDK1.5中提供了对枚举类型(Enum)的支持,使用枚举类型可以让我们把对某个属性的取值限制在给定范围之内,例如下面是对一个枚举类型的定义:
public enum MyColor{
    Red,
    Green,
    Blue
}
在使用Hibernate实现持久化时,由于其基本值类型中不包括枚举类型,所以不能对枚举类型直接进行映射,我们需要采用其它方式来实现枚举类型数据的持久化。
使用PersistentEnum
在Hibernate2中可以使用PersistentEnum接口来实现把枚举类型存储为整型数据。严格的说,这个枚举类型并不是JDK1.5中的Enum类型,而是Hibernate中的PersistentEnum,写一个实现了PersistentEnum接口的类,该类实现枚举类型的性质。如下示例(节选自hibernate文档):
package eg;
import net.sf.hibernate.PersistentEnum;
 
public class Color implements PersistentEnum {
    private final int code;
    private Color(int code) {
        this.code = code;
    }
    public static final Color TABBY = new Color(0);
    public static final Color GINGER = new Color(1);
    public static final Color BLACK = new Color(2);
 
    public int toInt() { return code; }
 
    public static Color fromInt(int code) {
        switch (code) {
            case 0: return TABBY;
            case 1: return GINGER;
            case 2: return BLACK;
            default: throw new RuntimeException("Unknown color code");
        }
    }
}
 
其中toInt()和fromInt()是PersistentEnum中定义的方法。然后在映射文件中用枚举类的名字作为类型名,此例中就是eg.Color。
个人认为这种实现方式不太合理,它把枚举类型与Hibernate紧密结合在了一起,假如以后我们的持久层不使用hibernate呢?庆幸的是Hibernate3中已经废弃掉了PersistentEnum。
使用自定义值类型
Hibernate提供了自定义值类型机制,作为对基本值类型映射的补充,用来满足开发者创建属于他们自己的值类型并对其进行映射。在org.hibernate.usertype包中定义了若干自定义值类型的接口,接下来的示例向大家展示了使用UserType接口来把枚举类型持久化为数据库中的VARCHAR类型字段。以我们前面所定义的MyColor枚举类型为例,只要我们为该枚举类型写一个实现了UserType接口的类即可,如下:
public class MyColorStringUserType implements UserType {
 
    private Class clazz = MyColor.class;
    private static final int[] sqlTypes = {Types.VARCHAR};
 
    public int[] sqlTypes() {
        return sqlTypes;
    }
 
    public Class returnedClass() {
        return clazz;
    }
 
    public boolean equals(Object x, Object y) throws HibernateException {
        if(x == y)
            return true;
        if(x == null || y == null)
            return false;
        else
            return x.equals(y);
    }
 
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }
 
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
        String value = rs.getString(names[0]);
        Object result = null;
        if(!rs.wasNull())
            result = Enum.valueOf(clazz, value);
        return result;
    }
 
    public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
        if(null == value)
            st.setNull(index, Types.VARCHAR);
        else
            st.setString(index, ((Enum)value).name());
    }
 
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }
 
    public boolean isMutable() {
        return false;
    }
 
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable)value;
    }
 
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }
 
    public Object replace(Object original, Object targer, Object owner) throws HibernateException {
        return original; 
    }
}
在配置时只要如下配置即可:
<property name="color" type="MyColorStringUserType" not-null="true"/>
采用这种方式,我们自己的枚举类型不依赖于hibernate,当使用hibernate实现持久化时,为相应的枚举类型写一个如上所述的实现了UserType接口的包装类即可。
从上面所实现的MyColorStringUserType类代码中我们可以发现,其中很多代码都是与特定的枚举类型MyColor无关的,当我们的系统中有很多枚举类型需要持久化时,难道我们在每个枚举类型相应的包装类中都重复编写这些代码吗?当然不是,这时我们可以使用JDK1.5中的范型抽取一个基类,如下:
public class EnumStringUserType<E extends Enum<E>> implements UserType {
 
    private Class<E> clazz = null;
         
    private static final int[] sqlTypes = {Types.VARCHAR};
         
    protected EnumStringUserType(Class<E> c){
         clazz = c;
    }
 
    public int[] sqlTypes() {
        return sqlTypes;
    }
 
    public Class returnedClass() {
        return clazz;
    }
 
    public boolean equals(Object x, Object y) throws HibernateException {
        if(x == y)
            return true;
        if(x == null || y == null)
            return false;
        else
            return x.equals(y);
    }
 
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }
 
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
        String value = rs.getString(names[0]);
        Object result = null;
        if(!rs.wasNull())
            result = Enum.valueOf(clazz, value);
        return result;
    }
 
    public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
        if(null == value)
            st.setNull(index, Types.VARCHAR);
        else
            st.setString(index, ((Enum)value).name());
    }
 
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }
 
    public boolean isMutable() {
        return false;
    }
 
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable)value;
    }
 
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }
 
    public Object replace(Object original, Object targer, Object owner) throws HibernateException {
        return original; 
    }
}
然后将其它枚举类型的自定义值类型继承基类 EnumStringUserType 即可,如:
public class MyColorStringUserType extends EnumStringUserType <MyColor>{
    public MyColorStringUserType () {
        super(MyColor.class);
    }
}
 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值