Hibernate 의 UserType 확장을 이용하여 Enum 타입의 객체를 원하는 값으로 맵핑하고자 한다.
즉, 다음과 같은 Enum 이 있을 경우
실제 데이터베이스에는 int 값으로 ( 100, 80,50,30) 이런식으로 저장되기를 원하고, 프로그램에서는 enum 타입으로 사용하고자
할때 Hibernate의 UserType을 확장하여 사용할 수 있다.
구글링으로 다음과 같은 GenericEnumUserType 을 찾아서 적용해 보았다.
다음과 같이 설정하고 로컬 테스트를 해 보면 오류가 발생한다.
해결 방안은 사용하고자 하는 Enum class의 메소드를 static 으로 선언해 주면 해결된다.
이렇게 해결은 되었지만 원인은 아직 불분명하다.
GenericEnumUserType.nullSafeGet에서 Reflection API를 사용하는데 아마도 Classloader가 달라서 발생하는 오류인건지?
Enum의 valueOf 메소드는 static 인데 그래서 그런 걸까?
그런데 발생하는 오류는 Reflection API에서 메소드 파라미터가 다르다는 오류인데....
시간내서 원인을 분석해 봐야 겠다.
참조:
http://community.jboss.org/wiki/Java5EnumUserType
http://code-ref.blogspot.com/2009/06/hibernate-genericenumusertype.html
즉, 다음과 같은 Enum 이 있을 경우
public enum EventGranularity { VERY_FINE(new Integer(1000)), FINE(new Integer(100)), COARSE( new Integer(10)), VERY_COARSE(new Integer(1)); private Integer intValue; public Integer getGranularity() { return intValue; } private EventGranularity(Integer intValue) { this.intValue = intValue; } public EventGranularity fromInt(Integer value) { switch (value) { case 1: return VERY_COARSE; case 10: return COARSE; case 100: return FINE; case 1000: return VERY_FINE; default: return VERY_COARSE; } } }
실제 데이터베이스에는 int 값으로 ( 100, 80,50,30) 이런식으로 저장되기를 원하고, 프로그램에서는 enum 타입으로 사용하고자
할때 Hibernate의 UserType을 확장하여 사용할 수 있다.
구글링으로 다음과 같은 GenericEnumUserType 을 찾아서 적용해 보았다.
public class GenericEnumUserType implements UserType, ParameterizedType { private static final long serialVersionUID = -1854479466843620961L; private static final String DEFAULT_IDENTIFIER_METHOD_NAME = " name";="" private="" static="" final="" string="" default_value_of_method_name="valueOf" ;="" class enumClass; private Class identifierType; private Method identifierMethod; private Method valueOfMethod; private NullableType type; private int[] sqlTypes; public void setParameterValues(Properties parameters) { String enumClassName = parameters.getProperty("enumClass"); try { enumClass = Class.forName(enumClassName).asSubclass(Enum.class); } catch (ClassNotFoundException cfne) { throw new HibernateException("Enum class not found", cfne); } String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME); try { identifierMethod = enumClass.getMethod(identifierMethodName, new Class[0]); identifierType = identifierMethod.getReturnType(); } catch (Exception e) { throw new HibernateException("Failed to obtain identifier method", e); } type = (NullableType) TypeFactory.basic(identifierType.getName()); if (type == null) throw new HibernateException("Unsupported identifier type " + identifierType.getName()); sqlTypes = new int[] { type.sqlType() }; String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME); try { valueOfMethod = enumClass.getMethod(valueOfMethodName, new Class[] { identifierType }); } catch (Exception e) { throw new HibernateException("Failed to obtain valueOf method", e); } } public Class returnedClass() { return enumClass; } public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException { Object identifier = type.get(rs, names[0]); if (rs.wasNull()) { return null; } try { return valueOfMethod.invoke( enumClass, new Object[] {identifier}); } catch (Exception e) { throw new HibernateException("Exception while invoking valueOf method '" + valueOfMethod.getName() + "' of " + "enumeration class '" + enumClass + "'", e); } } public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException { try { if (value == null) { st.setNull(index, type.sqlType()); } else { Object identifier = null; if (value instanceof String) { identifier = value; } else { identifier = identifierMethod.invoke(value, new Object[0]); } type.nullSafeSet(st, identifier, index, null); } } catch (Exception e) { throw new HibernateException("Exception while invoking identifierMethod '" + identifierMethod.getName() + "' of " + "enumeration class '" + enumClass + "'", e); } } public int[] sqlTypes() { return sqlTypes; } public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } public Object deepCopy(Object value) throws HibernateException { return value; } public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } public boolean equals(Object x, Object y) throws HibernateException { return x == y; } public int hashCode(Object x) throws HibernateException { return x.hashCode(); } public boolean isMutable() { return false; } public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } }
다음과 같이 설정하고 로컬 테스트를 해 보면 오류가 발생한다.
@Type( type="cong.hibernate.type.GenericEnumUserType" , parameters= { @Parameter (name= "enumClass" , value="cong.hibernate.EventGranularity"), @Parameter (name= "identifierMethod" , value="getGranularity"), @Parameter (name= "valueOfMethod" , value="fromInt") } ) private EventGranularity granularity;
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at cong.hibernate.GenericEnumUserType.nullSafeGet(GenericEnumUserType.java:82)
... 51 more
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at cong.hibernate.GenericEnumUserType.nullSafeGet(GenericEnumUserType.java:82)
... 51 more
해결 방안은 사용하고자 하는 Enum class의 메소드를 static 으로 선언해 주면 해결된다.
public static EventGranularity fromInt(Integer value) { switch (value) { case 1: return VERY_COARSE; case 10: return COARSE; case 100: return FINE; case 1000: return VERY_FINE; default: return VERY_COARSE; } }
이렇게 해결은 되었지만 원인은 아직 불분명하다.
GenericEnumUserType.nullSafeGet에서 Reflection API를 사용하는데 아마도 Classloader가 달라서 발생하는 오류인건지?
Enum의 valueOf 메소드는 static 인데 그래서 그런 걸까?
그런데 발생하는 오류는 Reflection API에서 메소드 파라미터가 다르다는 오류인데....
시간내서 원인을 분석해 봐야 겠다.
참조:
http://community.jboss.org/wiki/Java5EnumUserType
http://code-ref.blogspot.com/2009/06/hibernate-genericenumusertype.html