Member Menu
 
 Monthly JBoss newsletter:
 
Hibernate Books
CaveatEmptor

Java 5 EnumUserType

Hibernate 3 Parameterized type for mapping a Java 5 Enum.

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.

Example Mapping - inline <type> tag

  <property name='suit'>
    <type name="EnumUserType">
      <param name="enumClassName">com.company.project.Suit</param>
    </type>
  </property>

Example Mapping - using <typedef>

  <typedef name="suit" class='EnumUserType'>
      <param name="enumClassName">com.company.project.Suit</param>
  </typedef>

  <class ...>
    <property name='suit' type='suit'/>
  </class>

EnumUserType.java

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 org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

public class EnumUserType implements UserType, ParameterizedType {
   
   private Class clazz = null;
   
   public void setParameterValues(Properties params) {
      String enumClassName = params.getProperty("enumClassName");
      if (enumClassName == null) {
         throw new MappingException("enumClassName parameter not specified");
      }
      
      try {
            this.clazz = Class.forName(enumClassName);
        } catch (java.lang.ClassNotFoundException e) {
         throw new MappingException("enumClass " + enumClassName + " not found", e);
        }
   }
   
    private static final int[] SQL_TYPES = {Types.VARCHAR};
    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    public Class returnedClass() {
        return clazz;
    }

    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
                             throws HibernateException, SQLException {
        String name = resultSet.getString(names[0]);
        Object result = null;
        if (!resultSet.wasNull()) {
            result = Enum.valueOf(clazz, name);
        }
        return result;
    }

   public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) 
                          throws HibernateException, SQLException {
        if (null == value) {
            preparedStatement.setNull(index, Types.VARCHAR);
        } else {
            preparedStatement.setString(index, value.name());
        }
    }

    public Object deepCopy(Object value) throws HibernateException{
        return value;
    }

    public boolean isMutable() {
        return false;
    }

    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable)value;
    }

    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }
    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y)
            return true;
        if (null == x || null == y)
            return false;
        return x.equals(y);
    }
}

Enhanced UserType

Here is my version of this, it's an EnhancedUserType which means it can be used for an id or a discriminator

package org.hibernate.demo;

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 org.hibernate.HibernateException;
import org.hibernate.usertype.EnhancedUserType;
import org.hibernate.usertype.ParameterizedType;

/**
 * @author Gavin King
 */
public class EnumUserType implements EnhancedUserType, ParameterizedType {
    
    private Class<Enum> enumClass;

    public void setParameterValues(Properties parameters) {
        String enumClassName = parameters.getProperty("enumClassName");
        try {
            enumClass = (Class<Enum>) Class.forName(enumClassName);
        }
        catch (ClassNotFoundException cnfe) {
            throw new HibernateException("Enum class not found", cnfe);
        }
    }

    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 (Enum) 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 nullSafeGet(ResultSet rs, String[] names, Object owner) 
    throws HibernateException, SQLException {
        String name = rs.getString( names[0] );
        return rs.wasNull() ? null : Enum.valueOf(enumClass, name);
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index) 
    throws HibernateException, SQLException {
        if (value==null) {
            st.setNull(index, Types.VARCHAR);
        }
        else {
            st.setString( index, ( (Enum) value ).name() ); 
        }
    }

    public Object replace(Object original, Object target, Object owner) 
    throws HibernateException {
        return original;
    }

    public Class returnedClass() {
        return enumClass;
    }

    public int[] sqlTypes() {
        return new int[] { Types.VARCHAR };
    }

    public Object fromXMLString(String xmlValue) {
        return Enum.valueOf(enumClass, xmlValue);
    }

    public String objectToSQLString(Object value) {
        return '\'' + ( (Enum) value ).name() + '\'';
    }

    public String toXMLString(Object value) {
        return ( (Enum) value ).name();
    }

}

Flexible solution

The version I like to present deals with various types of enum representations. It successfully handles int, long and string based enumeration patterns. The flexibilty is introduced by the identifierMethod and the valueOfMethod properties of the user type mapping - which are optional (check the code). Check out the JavaDoc comment for a simple example. If you find bugs or gaps, please drop me a line. I personally would also refactor the longer setParameters method, but this version is more compact.

import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.type.NullableType;
import org.hibernate.type.TypeFactory;
import org.hibernate.usertype.ParameterizedType;

/**
 * Implements a generic enum user type identified / represented by a single identifier / column.
 * <p><ul>
 *    <li>The enum type being represented by the certain user type must be set
 *        by using the 'enumClass' property.</li>
 *    <li>The identifier representing a enum value is retrieved by the identifierMethod.
 *        The name of the identifier method can be specified by the 
 *        'identifierMethod' property and by default the name() method is used.</li>
 *    <li>The identifier type is automatically determined by 
 *        the return-type of the identifierMethod.</li>
 *    <li>The valueOfMethod is the name of the static factory method returning
 *        the enumeration object being represented by the given indentifier. 
 *        The valueOfMethod's name can be specified by setting the 'valueOfMethod'
 *        property. The default valueOfMethod's name is 'valueOf'.</li>
 * </p> 
 * <p>
 * Example of an enum type represented by an int value:
 * <code>
 * public enum SimpleNumber {
 *   Unknown(-1), Zero(0), One(1), Two(2), Three(3);
 *   int value;
 *   protected SimpleNumber(int value) {
 *       this.value = value;
 *       }
 *
 *   public int toInt() { return value; }
 *   public SimpleNumber fromInt(int value) {
 *         switch(value) {
 *          case 0: return Zero;
 *         case 1: return One;
 *         case 2: return Two;
 *         case 3: return Three;
 *         default:
 *                 return Unknown;
 *     }
 *   }
 * }
 * </code>
 * <p>
 * The Mapping would look like this:
 * <code>
 *    <typedef name="SimpleNumber" class="GenericEnumUserType">
 *        <param name="enumClass">SimpleNumber</param>
 *        <param name="identifierMethod">toInt</param>
 *        <param name="valueOfMethod">fromInt</param>
 *    </typedef>
 *    <class ...>
 *      ...
 *     <property name="number" column="number" type="SimpleNumber"/>
 *    </class>
 * </code>
 *
 * @author Martin Kersten
 * @since 05.05.2005
 */

public class GenericEnumUserType extends AbstractUserType implements ParameterizedType  {
    private Class<? extends Enum> enumClass;
    private Class<?> identifierType;

    private Method identifierMethod;
    private Method valueOfMethod;
    
    private static final String defaultIdentifierMethodName = "name";
    private static final String defaultValueOfMethodName = "valueOf";
    
    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 exception) {
            throw new HibernateException("Enum class not found", exception);
        }
        
        String identifierMethodName = 
            parameters.getProperty("identifierMethod", defaultIdentifierMethodName);

        try {
            identifierMethod = enumClass.getMethod(identifierMethodName, new Class[0]);
            identifierType = identifierMethod.getReturnType();
        }
        catch(Exception exception) {
            throw new HibernateException("Failed to optain identifier method", exception);
        }
        
        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", defaultValueOfMethodName);
        
        try {
            valueOfMethod = enumClass.getMethod(
                    valueOfMethodName, new Class[] { identifierType });
        } 
        catch(Exception exception) {
            throw new HibernateException("Failed to optain valueOf method", exception);
        }
    }
    
    public Class returnedClass() {
        return enumClass;
    }
    
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) 
                        throws HibernateException, SQLException {
        Object identifier=type.get(rs, names[0]);
        try {
            return valueOfMethod.invoke(enumClass, new Object [] {identifier});
        }
        catch(Exception exception) {
            throw new HibernateException(
                    "Exception while invoking valueOfMethod of enumeration class: ", exception);
        } 
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index) 
            throws HibernateException, SQLException {
        try {
            Object identifier = value != null ? identifierMethod.invoke(value, new Object[0]) : null;
            st.setObject(index, identifier);
        }
        catch(Exception exception) {
            throw new HibernateException(
                    "Exception while invoking identifierMethod of enumeration class: ", exception);

        } 
    }
    public int[] sqlTypes() {
        return sqlTypes;
        //There was a logical bug within the set-up phase of any user type
        //I reported the issue and it got instantly solved (Thanks again Garvin!)
        //But it might still exist in your Hibernate version. So if you are 
        //facing any null-pointer exceptions, use the return statement below. 
        //Note: INTEGER works even for String based mappings...
        //return new int [] {Types.INTEGER};
    }
 
        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;
    }
}

Flexible solution - working version

Even after applying the various recommended fixes from the comments, I still found that Martin Kersten's GenericEnumUserType had problems in some cases. After adding some additional tweaks I now have a version that seems to work great. Enjoy!

import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.type.NullableType;
import org.hibernate.type.TypeFactory;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;


public class GenericEnumUserType implements UserType, ParameterizedType {
    private static final String DEFAULT_IDENTIFIER_METHOD_NAME = "name";
    private static final String DEFAULT_VALUE_OF_METHOD_NAME = "valueOf";

    private Class<? extends Enum> 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 = identifierMethod.invoke(value, new Object[0]);
                type.set(st, identifier, index);
            }
        } 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;
    }
}

  NEW COMMENT

Suggestion 10 Mar 2005, 11:50 Goonie
Are your shure that value.toString() is the correct way to coerce an
Enum to its String value in nullSafeSet()?

Remember that toString() can be overridden by some "user friendly value".

Maybe Enum.name() would be safer?

Regards,

Andreas
 
Re: Suggestion 11 Mar 2005, 08:11 magic
On 10 Mar 2005 11:50, Goonie wrote: 
>Maybe Enum.name() would be safer?
 
You're right.  
Extracted from the java documentation (about Enum.name()):  
"This method is designed primarily for use in specialized situations  
where correctness depends on getting the exact name, which will not  
vary from release to release."
 
Bound mismatch in Enhanced UserType tip 19 Apr 2005, 08:37 dipp0
In Eclipse 3.1M6 (compared to M5), you get a bound mismatch error when 
using the "Class<Enum> enumClass" as argument to the "Enum.valueOf()" 
method.

"Bound mismatch: The generic method valueOf(Class<T>, String) of type 
Enum<E> is not applicable for the arguments (Class<Enum>, String) since 
the type Enum is not a valid substitute for the bounded parameter <T 
extends Enum<T>>"

Workaround: Declare the variable "Class enumClass" instead to avoid the 
error. There's still some warnings but at least it compiles. Also change 
the line "enumClass = (Class<Enum>) Class.forName(enumClassName);" to 
"enumClass = Class.forName(enumClassName);".
 
RE : Suggestion 20 Apr 2005, 22:47 cameronbraid
enum.toString() changed to enum.name() in EnumUserType.java
 
Re: Bound mismatch in Enhanced UserType tip 24 Apr 2005, 08:42 stephan.maier
Here's a solution to the problem (I just cpy the relevant lines):

private Class<? extends Enum> enumClass;

public void setParameterValues(Properties parameters) {
   String enumClassName = parameters.getPropert("enumClassName");
   try {
      enumClass = Class.forName(enumClassName).asSubclass(Enum.class);
   }
   catch (ClassNotFoundException cnfe) {
      throw new HibernateException("Enum class not found", cnfe);
   }
}

Observe the way I declared the attribute enumClass. also check the way 
I did the cast by using the method #asSubclass ont the Class-class. 
That should do the trick - it does it for me.

Stephan
 
Re: RE : Suggestion 25 Apr 2005, 20:14 shangqiao
if you want to store the Enum type with int,you can:
    public Object nullSafeGet(ResultSet resultSet, String[] names, 
Object owner)
            throws HibernateException, SQLException {
        Object result = null;
        try {
            int index = resultSet.getInt(names[0]);
            if (!resultSet.wasNull()) {
                Object[] enumValue = enumValues.get(this.getClass());
                if (enumValue == null) {
                    Method method = null;
                    method = clazz.getDeclaredMethod("values", new 
Class[0]);
                    enumValue = (Object[]) method.invoke(null, new 
Object[0]);
                    enumValues.put(this.getClass(), enumValue);
                }
                result = enumValue[index];
            }
        } catch (Exception e) {
            e.printStackTrace();
            result = null;
        }
        return result;
    }

    public void nullSafeSet(PreparedStatement preparedStatement, 
Object value,
            int index) throws HibernateException, SQLException {
        if (null == value) {
            preparedStatement.setNull(index, Types.VARCHAR);
        } else {
            preparedStatement.setInt(index, ((Enum) value).ordinal());
        }
    }
 
A proposal for an enhanced EnumUserType 18 May 2005, 13:26 cernautan
Here is a proposal for an enhanced EnumUserType that allow you to
specify if the Name or the Ordinal of an Enum constant should end up in
the database column:

public class EnumUserType implements EnhancedUserType, ParameterizedType {

    private Class<Enum> enumClass;
    private boolean useName;
    private final int ORDINAL_FOR_NULL = -1;

    public void setParameterValues(Properties parameters) {
        String enumClassName =
parameters.getProperty("enumClassName").trim();
        try {
            enumClass = (Class<Enum>) Class.forName(enumClassName);
        } catch (ClassNotFoundException cnfe) {
            throw new HibernateException("Enum class not found", cnfe);
        }
        String columnType = parameters.getProperty("columnType").trim();
        useName = !columnType.equalsIgnoreCase("INTEGER");
    }

    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 (Enum) 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;
    }

    public Class returnedClass() {
        return enumClass;
    }

    public int[] sqlTypes() {
        return new int[]{useName ? Types.VARCHAR : Types.INTEGER};
    }

    public Object nullSafeGet(ResultSet resultSet, String[] names,
Object owner)
            throws HibernateException, SQLException {
        if (useName) {
            String name = resultSet.getString(names[0]);
            return resultSet.wasNull() ? null : Enum.valueOf(enumClass,
name);
        } else {
            int ordinal = resultSet.getInt(names[0]);
            if (resultSet.wasNull() || (ordinal == ORDINAL_FOR_NULL)) {
                return null;
            } else {
                return enumClass.getEnumConstants()[ordinal];
            }
        }
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index)
            throws HibernateException, SQLException {
        if (useName) {
            if (value == null) {
                st.setNull(index, Types.VARCHAR);
            } else {
                st.setString(index, ((Enum) value).name());
            }
        } else {
            if (value == null) {
                st.setInt(index, ORDINAL_FOR_NULL);
            } else {
                st.setInt(index, ((Enum) value).ordinal());
            }
        }
    }

    public Object fromXMLString(String xmlValue) {
        if (useName) {
            return Enum.valueOf(enumClass, xmlValue);
        } else {
            int ordinal = Integer.parseInt(xmlValue);
            return enumClass.getEnumConstants()[ordinal];
        }
    }

    public String objectToSQLString(Object value) {
        if (useName) {
            return '\'' + ((Enum) value).name() + '\'';
        } else {
            return String.valueOf(((Enum) value).ordinal());
        }
    }

    public String toXMLString(Object value) {
        if (useName) {
            return ((Enum) value).name();
        } else {
            return String.valueOf(((Enum) value).ordinal());
        }
    }
}
 
introspecting the Class Name of the Enum? 05 Jun 2005, 08:04 Goonie
Shouldn't it be possible to introspect the class of the Enum by looking
at the type of the property?

Instead of having this Parameter called "enumClassName"?
 
SetUserType 10 Jun 2005, 17:28 Goonie
Is there also a "SetUserType" somewhere that maps a MySQL SET type to a
collection of enums?
 
For annotaion users: 21 Jun 2005, 22:56 steeven
@Column(length=20)
@Type(type = "platform.common.database.EnumUserType", 
	parameters = { @Parameter(
		name = "enumClassName", 
		value = "common.log.LogType") })
public LogType getLogType(){
	return this.logType;
}
 
Flexible EnumUserType 07 Jul 2005, 09:34 rwwilden
The code for the nullSafeGet method of the flexible EnumUserType (Martin
Kersten) should be null-safe, which it is not now. It should be:

public Object nullSafeGet(ResultSet resultSet, String[] names,
                          Object owner)
      throws SQLException {
   Object identifier = type.get(resultSet, names[0]);
   if (identifier == null) {
      return null;
   }
   else {
      try {
         return valueOfMethod.invoke(null, new Object[] { identifier });
      }
      catch (Exception e) {
         throw new HibernateException("Exception while invoking "
            + "valueOf method '" + valueOfMethod + "' on enum class '"
            + enumClass + "'.", e);
      }
   }
}
 
Another way, using Field Reflection 18 Aug 2005, 18:50 ZellmoTheMagnificent
The solution below needs no extra special attention. Just declare that
type in your mapping file as such:

<property name="status" column="STATUS"
    type="db.hibernate.EnumUserType"/>


/**
 * 
 */
package db.hibernate;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;

/**
 * <code>EnumUserType</code>
 * 
 * Created: Aug 18, 2005 5:58:31 PM
 */
public class EnumUserType implements UserType
{

    private static final int[] SQL_TYPES = { Types.VARCHAR };

    public EnumUserType()
    {
        super();
    }

    public int[] sqlTypes()
    {
        return SQL_TYPES;
    }

   public Class returnedClass()
    {
        return Enum.class;
    }

   public boolean equals(Object obj1, Object obj2) throws
HibernateException
    {
        if (obj1 == obj2)
            return true;
        if (obj1 == null || obj2 == null)
            return false;
        return ((Enum) obj1).equals(((Enum) obj2));
    }

   public int hashCode(Object object) throws HibernateException
    {
        return ((Enum) object).hashCode();
    }

   private String getRepresentation(Object object)
    {
        return object.getClass().getName() + " " + ((Enum) object).name();
    }

   private Object getObject(String representation) throws
HibernateException
    {
        try
        {
            String[] parts = representation.split(" ");
            Class c = Class.forName(parts[0]);
            Field field = c.getField(parts[1]);
            return field.get(null);
        }
        catch (Exception e)
        {
            throw new HibernateException(e);
        }
    }

   public Object nullSafeGet(ResultSet resultSet, String[] names,
Object owner)
            throws HibernateException, SQLException
    {
        if (resultSet.wasNull())
            return null;
        String value = resultSet.getString(names[0]);
        return getObject(value);
    }

   public void nullSafeSet(PreparedStatement statement, Object value,
int index)
            throws HibernateException, SQLException
    {
        if (value == null)
            statement.setNull(index, Types.VARCHAR);
        else
        {
            String representation = getRepresentation(value);
            statement.setString(index, representation);
        }
    }

   public Object deepCopy(Object value) throws HibernateException
    {
        return value;
    }

   public boolean isMutable()
    {
        return false;
    }

   public Serializable disassemble(Object object) throws HibernateException
    {
        return getRepresentation(object);
    }

   public Object assemble(Serializable serializable, Object owner)
            throws HibernateException
    {
        return getObject((String) serializable);
    }

   public Object replace(Object original, Object target, Object owner)
            throws HibernateException
    {
        return original;
    }

}
 
Re: Another way, using Field Reflection 27 Sep 2005, 19:21 phersh
Hi,

The nullSafeGet doesn't protect from nulls because 

String value = resultSet.getString(names[0]); can return null.

I've changed nullSafeGet to:
    public Object nullSafeGet(ResultSet resultSet, String[] names,
Object owner)
            throws HibernateException, SQLException {
        if (resultSet.wasNull()) return null;
        String value = resultSet.getString(names[0]);
        if (value == null) return null;
        return getObject(value);
    }
 
Re: Another way, using Field Reflection 06 Oct 2005, 18:16 lkline
POST QUESTIONS ON THE FORUM! COMMENTS HERE SHOULD ADD VALUE TO THE
PAGE!On 27 Sep 2005 19:21, phersh wrote:

>Hi,

>The nullSafeGet doesn't protect from nulls because

>String value = resultSet.getString(names[0]); can return null.

>I've changed nullSafeGet to:
>    public Object nullSafeGet(ResultSet resultSet, String[] names,
>Object owner)
>            throws HibernateException, SQLException {
>        if (resultSet.wasNull()) return null;
>        String value = resultSet.getString(names[0]);
>        if (value == null) return null;
>        return getObject(value);
>    }

I ran into a very subtle bug with this method.  Depending on the
ordering of the property definitions in my class mapping the call to
wasNull() would cause a SQLException.  I changed the method like this:

    public Object nullSafeGet(ResultSet resultSet, String[] names,
Object owner) throws HibernateException, SQLException {
        String value = resultSet.getString(names[0]);
        if (resultSet.wasNull())
            return null;
        return getObject(value);

Maybe this isn't so good if the column type is not 'string' compatible,
but it seems that is the only possibility for this class.

Here's the mapping that failed:
...
<class name="org.egcrc.cfr.hibernate.EnumTestClass" table="ENUM_TEST"
lazy="true">

  <id name="id" column="ID" type="long" unsaved-value="null">
    <generator class="native"/>
  </id>
  <property name="alone" type="org.egcrc.cfr.hibernate.EnumUserType"
column="ALONE"/>
  <property name="foo" type="string" column="FOO"/>
  <property name="internal" type="org.egcrc.cfr.hibernate.EnumUserType"
column="INTERNAL"/>
</class>
...
The class has three instance variables, two of which are Java 1.5 enums,
and one is string.
But if I change this mapping to put the foo property either before the
two enums or after them it works with the original code.  If you look at
the javadoc for ResultSet#wasNull() it states that you must have called
a column getter before calling it.  Perhaps that was not done?  I didn't
have the time to trace through the code in that much detail, but my
change did fix the problem.

I hope this helps someone avoid this problem.
 
Problem 06 Feb 2006, 19:24 optimusprime
Has anyone ever tried this? The method below throws an exception:

public void nullSafeSet(PreparedStatement st, Object value, int index) 
            throws HibernateException, SQLException {
        try {
            Object identifier = value != null ?
identifierMethod.invoke(value, new Object[0]) : null;
            st.setObject(index, identifier);
        }
        catch(Exception exception) {
            throw new HibernateException(
                    "Exception while invoking identifierMethod of
enumeration class: ", exception);

        } 
    }

Well, invoke shouldn`t be used with an instance and not a class
declaration? It's throwing an IllegalArgumentException:object is not an
instance of declaring class

If i hack it and put StateEnum.FL instead I get it working. As I don't
know enums quite enough I really do not have an answer for that, wonder
if someone does.

Regars
 
Please add this for ddl generation 01 Mar 2006, 00:48 hari_sujathan
I had to make following change in hbm.xml so that hibernate 
synchronizer generates DDL for me.
 <property name='suit' type="java.lang.String">
    <column name="suitType"  length="30"/>
    <type name="EnumUserType">
      <param name="enumClassName">com.company.project.Suit</param>
    </type>
  </property>
with original mapping file entry shown at top, hibernate synchroniser 
was not generating DDL, and I had to manually modify DDL after 
generating it with enum property commented out.

Rgds,
Hari Sujathan
 
EnumUserType doesn't compile.. here's the fix 31 May 2006, 13:43 famousactress
The .name() change from toString() was a good one, but there's a missing
case.. NullSafeSet should look like:

public void nullSafeSet(PreparedStatement preparedStatement, Object
value, int index) 
  throws HibernateException, SQLException {

  if (null == value) {
    preparedStatement.setNull(index, Types.VARCHAR);
  } else {
    preparedStatement.setString(index, ((Enum)value).name());
  }
}
 
Re: Problem 14 Jun 2006, 05:55 birkirb
This seems to have fixed the GenericEnumUserType for me.  Very handy.
-----

public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException 
{
  try
  {
    if(value == null)
    {
      st.setObject(index, null);
    }
    else
    {
      Object identifier = identifierMethod.invoke(value, new Object[0]);
      type.set(st,identifier,index);
    }
  }
  catch (Exception exception)
  {
    throw new HibernateException("Exception while invoking
identifierMethod '" + identifierMethod + "' of enumeration class: '" +
enumClass + "'",exception);
  }
}
 
Mapping the Enum Ordinal Instead of the String Value 15 Aug 2006, 15:53 crgardner
I modified Gavin's EnumUserType to map the ordinal instead of the string
value.

http://www.hibernate.org/312.733.html
 
Thanks! 21 Sep 2006, 02:01 scorchio96
The Flexible approach saved my bacon!
 
Thanks 25 Sep 2006, 00:22 batter
Using some ideas from the flexible solution I was getting an exception 
on the nullSaveGet method:

object is not an instance of declaring class.

So I changed:

valueOfMethod.invoke(enumClass, new Object [] {ordinal});

to

valueOfMethod.invoke(enumClass.getEnumConstants()[0], new Object [] 
{ordinal});

Did anyone else run into this?  Is there a better way of doing this?
 
Flexible solution - working version 28 Sep 2006, 15:01 cplehew
Even after applying the various recommended fixes in the comments the
Flexible Solution would still break for me in certain cases, so after
getting it working I decided to just add my version to the main Wiki
page.  This should save other folks the trouble...
 
Re: Flexible solution - working version 15 Nov 2006, 09:32 whiteman
On 28 Sep 2006 15:01, cplehew wrote:

>Even after applying the various recommended fixes in the comments the
>Flexible Solution would still break for me in certain cases, so after
>getting it working I decided to just add my version to the main Wiki
>page.  This should save other folks the trouble...

Can you plz point me the the URL where you put your version ?
I checked the WIKI page and did only find StringValuedEnumType on 
http://www.hibernate.org/273.html
Is it this one you are referring to ?
Thx, Jan
 
Re: Flexible solution - working version 15 Nov 2006, 16:53 cplehew
>Can you plz point me the the URL where you put your version ?
>I checked the WIKI page and did only find StringValuedEnumType on
>http://www.hibernate.org/273.html
>Is it this one you are referring to ?
>Thx, Jan

No, sorry...  I posted it on this page under the heading "Flexible
solution - working version".  The javadocs provided with the original
flexible solution above it still apply, by the way.
 
Another way for fixing error "object is not an instance of decla 16 Nov 2006, 11:59 poinsarx
Another way for fixing java.lang.IllegalArgumentException: object is not
an instance of declaring class :
By modifying the valueOf() as a static method : the error is fixed and
this is not an heresy.
 
Re: Flexible solution - working version 07 Apr 2008, 02:42 avinash.thm
POST QUESTIONS ON THE FORUM! COMMENTS HERE SHOULD ADD VALUE TO THE PAGE!
On 15 Nov 2006 16:53, cplehew wrote:

>>Can you plz point me the the URL where you put your version ?
>>I checked the WIKI page and did only find StringValuedEnumType on
>>http://www.hibernate.org/273.html
>>Is it this one you are referring to ?
>>Thx, Jan

>No, sorry...  I posted it on this page under the heading "Flexible
>solution - working version".  The javadocs provided with the original
>flexible solution above it still apply, by the way.


Can somebody please let me know how to configure EnumUserType using 
annotations?
 
Re: Flexible solution - working version 09 Apr 2008, 09:50 roundhead
On 07 Apr 2008 02:42, avinash.thm wrote:
...
>Can somebody please let me know how to configure EnumUserType using
>annotations?

One suggestion is at
http://appfuse.org/display/APF/Java+5+Enums+Persistence+with+Hibernate
 
© Copyright 2006, Red Hat Middleware, LLC. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc. [Privacy Policy]