在編碼過程當中,常常會遇到用某個數值來表示某種狀態、類型或者階段的狀況,好比有這樣一個枚舉: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