Version 3

    Hibernate 3 Parameterized type for mapping a Java 5 Enum with string values.

    This allows you to avoid the need to define a concrete UserType instance for every enum that you have. Just create a new typedef for each one, giving it a unique type name. Then reference this type name in the property tag.

    Unlike other proposals this class allows for the value in the database to be diferent from the Enum constant name.

     

    Interface for making the access to the string value uniform

    /**
     * Utility class designed to allow dinamic fidding and manipulation of Enum 
     * instances which hold a string value.
     */
    public interface StringValuedEnum {    
        
        /**
         * Current string value stored in the enum.
         * @return string value.
         */
        public String getValue();
        
    }
    

     

    Reflecting Enums to get the string value

    /**
     * Utility class designed to inspect StringValuedEnums.
     */
    public class StringValuedEnumReflect {
        
        /**
         * Don't let anyone instantiate this class.
         * @throws UnsupportedOperationException Always.
         */
        private StringValuedEnumReflect() {
            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>> T[] 
                getValues(Class<T> enumClass){
            return enumClass.getEnumConstants();
        }
        
        /**
         * All possible string values of the string valued enum.
         * @param enumClass Class to reflect.
         * @return Available string values.
         */
        public static <T extends Enum<T> & StringValuedEnum> String[] 
                getStringValues(Class<T> enumClass){ 
            T[] values = getValues(enumClass);
            String[] result = new String[values.length];
            for (int i=0; i<values.length; i++){
                result[i] = values[i].getValue();
            }
            return result;
        }
        
        /**
         * Name of the enum instance which hold the especified string value.
         * If value has duplicate enum instances than returns the first occurency.
         * @param enumClass Class to inspect.
         * @param value String.
         * @return name of the enum instance.
         */
        public static <T extends Enum<T> & StringValuedEnum> String 
                getNameFromValue(Class<T> enumClass, String value){
            T[] values = getValues(enumClass);
            for (T v : values){
                if (v.getValue().equals(value)){
                    return v.name();
                }
            }
            return "";
        }
        
    }
    

     

    StringValuedEnumType

    import java.io.Serializable;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Types;
    import java.util.Properties;
    import java.lang.reflect.*;
    
    import org.hibernate.HibernateException;
    import org.hibernate.usertype.EnhancedUserType;
    import org.hibernate.usertype.ParameterizedType;
    
    import static StringValuedEnumReflect.*;
    
    //Please notice the calls to getNameFromValue *************************
    public class StringValuedEnumType <T extends Enum<T> & StringValuedEnum> 
            implements EnhancedUserType, ParameterizedType{
        
        /**
         * Enum class for this particular user type.
         */
        private Class<T> enumClass;
    
        /**
         * Value to use if null.
         */
        private String defaultValue;
        
        /** Creates a new instance of ActiveStateEnumType */
        public StringValuedEnumType() {
        }
        
        public void setParameterValues(Properties parameters) {
            String enumClassName = parameters.getProperty("enum");
            try {
                enumClass = (Class<T>) Class.forName(enumClassName).asSubclass(Enum.class).
                        asSubclass(StringValuedEnum.class); //Validates the class but does not eliminate the cast
            } catch (ClassNotFoundException cnfe) {
                throw new HibernateException("Enum class not found", cnfe);
            }
    
            setDefaultValue(parameters.getProperty("defaultValue"));
        }
    
        public String getDefaultValue() {
            return defaultValue;
        }
        
        public void setDefaultValue(String 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.VARCHAR };
        }
        
        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 HibernateException, SQLException {
            String value = rs.getString( names[0] );
            if (value==null) {
                value = getDefaultValue();
                if (value==null){ //no default value
                    return null;
                } 
            }
            String name = 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 HibernateException, SQLException {
            if (value==null) {
                st.setNull(index, Types.VARCHAR);
            } else {
                st.setString( index, ((T) value).getValue() );
            }
        }
        
        public Object assemble(Serializable cached, Object owner)
                throws HibernateException {
            return cached;
        }
        
        public Serializable disassemble(Object value) throws HibernateException {
            return (Enum) value;
        }
            
        public Object deepCopy(Object value) throws HibernateException {
            return value;
        }
    
        public boolean equals(Object x, Object y) throws HibernateException {
            return x==y;
        }
        
        public int hashCode(Object x) throws HibernateException {
            return x.hashCode();
        }
    
        public Object replace(Object original, Object target, Object owner)
                throws HibernateException {
            return original;
        }
    
        public String objectToSQLString(Object value) {
            return '\'' + ((T) value).getValue() + '\'';
        }
        
        public String toXMLString(Object value) {
            return ((T) value).getValue();
        }
    
        public Object fromXMLString(String xmlValue) {
            String name = getNameFromValue(enumClass, xmlValue);
            return Enum.valueOf(enumClass, name);
        }
            
    }
    

     

    Example Enum

    /**
     * Common mapping for Active/Inactive state fields. This class is only usefull
     * when the field respect the mapping defined here.
     */
    public enum ActiveState implements StringValuedEnum{
        
        /** 
         * Active state.
         */
        ACTIVE("A"),
        
        /** 
         * Inactive state
         */
        INACTIVE("I");
    
        /** 
         * Internal storage of state field value, please see the Enum spec for clarification.
         */ 
        private final String state;
        
        /** 
         * Enum constructor for ActiveState.
         * @param state Value of state.
         */
        ActiveState(final String state){
            this.state = state;
        }
    
        /**
         * Current string value stored in the enum.
         * @return string value.
         */
        public String getValue() {
            return this.state;
        }
        
    }
    

    (2005-06-29 Added Default value and corrected nullSafeGet (was not safe for nulls)