在编码过程当中,常常会遇到用某个数值来表示某种状态、类型或者阶段的状况,好比有这样一个枚举:html
public enum ComputerState { OPEN(10), //开启 CLOSE(11), //关闭 OFF_LINE(12), //离线 FAULT(200), //故障 UNKNOWN(255); //未知 private int code; ComputerState(int code) { this.code = code; } }
一般咱们但愿将表示状态的数值存入数据库,即ComputerState.OPEN
存入数据库取值为10
。java
首先,咱们先看看Hibernate是否可以知足咱们的需求。
Hibernate内置了org.hibernate.type.EnumType
转换器,可将枚举转换为Named
或Ordinal
。
这样使用它:sql
// 将ComputerState.OPEN转换OPEN @Enumerated(EnumType.STRING) private ComputerState state;
// ComputerState.OPEN转换为0,ComputerState.CLOSE转换为1 @Enumerated(EnumType.STRING) private ComputerState state;
以上的两种方式不能知足咱们的需求,看起来要本身实现转换的过程了。数据库
首先,咱们须要作一些准备工做,便于在枚举和code
之间转换。segmentfault
咱们须要一个接口来肯定某部分枚举类的行为。以下:缓存
public interface BaseCodeEnum { int getCode(); }
该接口只有一个返回编码的方法,返回值将被存入数据库。sass
就拿上面的ComputerState
来实现BaseCodeEnum
接口:session
public enum ComputerState implements BaseCodeEnum{ OPEN(10), //开启 CLOSE(11), //关闭 OFF_LINE(12), //离线 FAULT(200), //故障 UNKNOWN(255); //未知 private int code; ComputerState(int code) { this.code = code; } @Override public int getCode() { return this.code; } }
如今咱们能顺利的将枚举转换为某个数值了,还须要一个工具将数值转换为枚举实例。ide
public class CodeEnumUtil { public static <E extends Enum<?> & BaseCodeEnum> E codeOf(Class<E> enumClass, int code) { E[] enumConstants = enumClass.getEnumConstants(); for (E e : enumConstants) { if (e.getCode() == code) return e; } return null; } }
至此,准备工做完成。接下来须要在Hibernate中完成对枚举的转换。工具
Hibernate提供了javax.persistence.AttributeConverter<X,Y>
接口指定如何将实体属性转换为数据库列表示。
此方案适用与数量很少或者个别特殊的枚举。
须要实现两个方法:
public Y convertToDatabaseColumn (X attribute);
public X convertToEntityAttribute (Y dbData);
我是这样实现的:
public class CodeEnumConverter implements AttributeConverter<ComputerState,Integer> { @Override public Integer convertToDatabaseColumn(ComputerState attribute) { return attribute.getCode(); } @Override public ComputerState convertToEntityAttribute(Integer dbData) { return CodeEnumUtil.codeOf(ComputerState.class,dbData); } }
这样使用:
@Convert( converter = CodeEnumConverter.class ) private ComputerState state;
除了AttributeConverter
还提供了一个用户自定义类型的接口:org.hibernate.usertype.UserType
。
注意! 这里的类型不是一个实际的属性类型,而是一个知道如何将数据类型序列化到JDBC的类!
此方案适用于具备类似行为的一组枚举。
须要实现如下方法:
public int[] sqlTypes()
public Class returnedClass()
public boolean equals(Object x, Object y) throws HibernateException;
public int hashCode(Object x) throws HibernateException;
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException;
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException;
public Object deepCopy(Object value) throws HibernateException;
public boolean isMutable();
public Serializable disassemble(Object value) throws HibernateException;
public Object assemble(Serializable cached, Object owner) throws HibernateException;
public Object replace(Object original, Object target, Object owner) throws HibernateException;
我是这样实现的(参考了org.hibernate.type.EnumType
):
public class CodeEnumType<E extends Enum<?> & BaseCodeEnum> implements UserType, DynamicParameterizedType { private static final int SQL_TYPE = Types.INTEGER; private static final String ENUM = "enumClass"; private Class<E> enumClass; @Override public void setParameterValues(Properties parameters) { final ParameterType reader = (ParameterType) parameters.get(PARAMETER_TYPE); if (reader != null) { enumClass = reader.getReturnedClass().asSubclass(Enum.class); } else { final String enumClassName = (String) parameters.get(ENUM); try { enumClass = ReflectHelper.classForName(enumClassName, this.getClass()).asSubclass(Enum.class); } catch (ClassNotFoundException exception) { throw new HibernateException("Enum class not found: " + enumClassName, exception); } } } @Override public int[] sqlTypes() { return new int[]{SQL_TYPE}; } @Override public Class returnedClass() { return enumClass; } @Override public boolean equals(Object x, Object y) throws HibernateException { return x == y; } @Override public int hashCode(Object x) throws HibernateException { return x == null ? 0 : x.hashCode(); } @Override public E nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { final int value = rs.getInt(names[0]); return rs.wasNull() ? null : codeOf(value); } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { st.setObject(index, ((BaseCodeEnum) value).getCode(), SQL_TYPE); } @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public boolean isMutable() { return false; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } private E codeOf(int code) { try { return CodeEnumUtil.codeOf(enumClass, code); } catch (Exception ex) { throw new IllegalArgumentException("Cannot convert " + code + " to " + enumClass.getSimpleName() + " by code value.", ex); } } }
其中实现了DynamicParameterizedType.setParameterValues
方法,是为了获取具体的子类。
这样使用:
@Type(type = "com.example.CodeEnumType") private ComputerState state;
很久没有摸Hibernate了,生疏了不少。若是你还有更优的解决方案,请必定在评论中告知,万分感激。
在Mybatis中使用枚举能够看这里
参考资料:
Hibernate User Guide