前言
在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);
}
}