冰云 2010-04-11
当实体中包含类似XxxxType字段时,为了性能上的考虑,一般数据库里的字段类型为TINYINT或INT。而自从Java提供枚举类型以后,在Java代码中使用枚举类型来表示这种变量会更好。于是自然要在Hibernate里把TINYINT字段映射到枚举类型。我对Hibernate也不熟,网上找了一下发现内容很少,而且能找到的大多是把枚举类型映射到字符串类型的字段。挺奇怪的,难道大家还是在用定义一堆finalstaticint的常量来表示XxxxType么?
不管怎样,只好根据找到的最靠谱的代码再修改了一下。最终效果还不错。Bean的写法如下,枚举类型需要实现IntegerValuedEnum接口即可。
public class Test { public enum TestType implements IntegerValuedEnum { Type1(1), Type2(2), Type3(3); private final int code; private TestType(int code) { this.code = code; } @Override public int getCode() { return code; } } private int id; private TestType testType; // Other setters and getters }
在映射xml文件中如下配置testType:
<property name="testType" column="TEST_TYPE"> <type name="com.buzzinate.bshare.hibernate.IntegerValuedEnumType"> <param name="enum">com.buzzinate.bshare.beans.Test$TestType</param> </type> </property>
以下是实现代码。
IntegerValuedEnum接口:
public interface IntegerValuedEnum { int getCode(); }
IntegerValuedEnumReflect类
/** * Utility class designed to inspect IntegerValuedEnums. */ public final class IntegerValuedEnumReflect { /** * Don't let anyone instantiate this class. * * @throws UnsupportedOperationException * Always. */ private IntegerValuedEnumReflect() { throw new UnsupportedOperationException("This class must not be instanciated."); } /** * All Enum constants (instances) declared in the specified class. * * @param enumClass Class to reflect * @return Array of all declared EnumConstants (instances). */ private static <T extends Enum> T[] getValues(Class<T> enumClass) { return enumClass.getEnumConstants(); } /** * All possible string values of the string valued enum. * * @param enumClass Class to reflect. * @return Available integer values. */ public static <T extends Enum & IntegerValuedEnum> int[] getStringValues( Class<T> enumClass) { T[] values = getValues(enumClass); int[] result = new int[values.length]; for (int i = 0; i < values.length; i++) { result[i] = values[i].getCode(); } return result; } /** * Name of the enum instance which hold the respecified string value. If * value has duplicate enum instances than returns the first occurrence. * * @param enumClass Class to inspect. * @param value The int value. * @return name of the enum instance. */ public static <T extends Enum & IntegerValuedEnum> String getNameFromValue( Class<T> enumClass, int value) { T[] values = getValues(enumClass); for (int i = 0; i < values.length; i++) { if (values[i].getCode() == value) { return values[i].name(); } } return ""; } }
IntegerValuedEnumType类,也是最主要的类,实现了Hibernate的UserType接口。
public class IntegerValuedEnumType<T extends Enum & IntegerValuedEnum> implements EnhancedUserType, ParameterizedType { /** * Enum class for this particular user type. */ private Class<T> enumClass; /** * Value to use if null. */ private Integer defaultValue; public IntegerValuedEnumType() { } public void setParameterValues(Properties parameters) { String enumClassName = parameters.getProperty("enum"); try { enumClass = (Class<T>) Class.forName(enumClassName).asSubclass(Enum.class) .asSubclass(IntegerValuedEnum.class); } catch (ClassNotFoundException e) { throw new HibernateException("Enum class not found", e); } String defaultValueStr = parameters.getProperty("defaultValue"); if (defaultValueStr != null && !defaultValueStr.isEmpty()) { try { setDefaultValue(Integer.parseInt(defaultValueStr)); } catch (NumberFormatException e) { throw new HibernateException("Invalid default value", e); } } } public Integer getDefaultValue() { return defaultValue; } public void setDefaultValue(Integer defaultValue) { this.defaultValue = defaultValue; } /** * The class returned by <tt>nullSafeGet()</tt>. * * @return Class */ public Class returnedClass() { return enumClass; } public int[] sqlTypes() { return new int[] { Types.TINYINT }; } public boolean isMutable() { return false; } /** * Retrieve an instance of the mapped class from a JDBC resultset. * Implementors should handle possibility of null values. * * @param rs a JDBC result set * @param names the column names * @param owner the containing entity * @return Object * @throws HibernateException * @throws SQLException */ public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException { Integer value = rs.getInt(names[0]); if (value == null) { value = getDefaultValue(); if (value == null) { // no default value return null; } } String name = IntegerValuedEnumReflect.getNameFromValue(enumClass, value); Object res = rs.wasNull() ? null : Enum.valueOf(enumClass, name); return res; } /** * Write an instance of the mapped class to a prepared statement. * Implementors should handle possibility of null values. A multi-column * type should be written to parameters starting from <tt>index</tt>. * * @param st a JDBC prepared statement * @param value the object to write * @param index statement parameter index * @throws HibernateException * @throws SQLException */ public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException { if (value == null) { st.setNull(index, Types.TINYINT); } else { st.setInt(index, ((T) value).getCode()); } } public Object assemble(Serializable cached, Object owner) { return cached; } public Serializable disassemble(Object value) { return (Enum) value; } public Object deepCopy(Object value) { return value; } public boolean equals(Object x, Object y) { return x == y; } public int hashCode(Object x) { return x.hashCode(); } public Object replace(Object original, Object target, Object owner) { return original; } public String objectToSQLString(Object value) { return '\'' + String.valueOf(((T) value).getCode()) + '\''; } public String toXMLString(Object value) { return String.valueOf(((T) value).getCode()); } public Object fromXMLString(String xmlValue) { Integer value = Integer.parseInt(xmlValue); String name = IntegerValuedEnumReflect.getNameFromValue(enumClass, value); return Enum.valueOf(enumClass, name); } }
Reference:
http://community.jboss.org/wiki/Java5StringValuedEnumUserType