Member Menu
 
 Monthly JBoss newsletter:
 
Hibernate Books
CaveatEmptor

UserType for persisting an Enum with a VARCHAR column

The following code is a 'template' to easy Enum (Java 1.5) mapping.

import java.io.Serializable; 
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; 

public class EnumUserType<E extends Enum<E>> implements UserType { 
    private Class<E> clazz = null; 
    protected EnumUserType(Class<E> c) { 
        this.clazz = c; 
    } 
 
    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]); 
        E 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, ((Enum)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); 
    } 
} 

Now, let's imagine you have the following Enum:

public enum MyEnum { 
    ENUM_A, 
    ENUM_B; 
}

Just create the following custom mapping type:

public class MyEnumUserType extends EnumUserType<MyEnum> { 
    public MyEnumUserType() { 
        super(MyEnum.class); 
    } 
}

And finally, in your mapping file:

<hibernate-mapping>
[...]
  <property name="sample" type="mypackage.MyEnumUserType" not-null="true"/>
[...]

Vincent


  NEW COMMENT

annnotations ? 24 Feb 2005, 06:19 revoltingdigits
hi there , any hints on how to get this working with annotations / 
is there going to be support for enums when working with annotations ? 

Thanks

Bryan
 
Possible mismatch 08 Mar 2005, 13:29 adamc
in the nullSafeGet() method you use

if (!resultSet.wasNull()) { 
    result = Enum.valueOf(clazz, name); 
}

Which works in terms of the <em>name</em> of the enum instance; however, 
in the nullSafeSet() method you use

preparedStatement.setString(index, value.toString());

which calls toString() on the enum object (if I understand what's going
on correctly).  Unless I'm missing something, the class as written will
fail for any enum where the result returned by name() doesn't match up
with the result returned by toString(), because what it reads from the
database won't match what it writes.
 
Re: Possible mismatch 11 Mar 2005, 08:25 magic
On 08 Mar 2005 13:29, adamc wrote: 
 
>in the nullSafeGet() method you use
 
>if (!resultSet.wasNull()) {
>    result = Enum.valueOf(clazz, name);
>}
 
>Which works in terms of the <em>name</em> of the enum instance;
however,  
>in the nullSafeSet() method you use
 
>preparedStatement.setString(index, value.toString());
 
You're right. I modified the source. Thanks.
 
Adding solution to code base 04 May 2005, 03:18 Martin Kersten
The code looks very good. I wonder if this code can be placed into the
normal distribution. I found only three implementors of the UserType
interface and saidly this wasn't in there. 

Would be nice to have such a support right out of the box.
 
and a mapping to int in the DB? 15 Jul 2005, 19:20 benoitx
On 04 May 2005 03:18, Martin Kersten wrote:

>The code looks very good. I wonder if this code can be placed into the
>normal distribution. I found only three implementors of the UserType
>interface and saidly this wasn't in there.

Would be great indeed!  This is the kind of code that one does not want
to see duplicated...

Has anyone managed to do something equivalent for a mapping to an int?
ie the ordinal may be?

Saving to the database is no issue but there are no equivalent to
Enum.valueOf(int...) and for some reason that I do not quite understand,
values() is not a method on Enum... How would one pass the enum to the
EnumUserType?

The problem is in 
    public Object nullSafeGet(ResultSet resultSet, String[] names,
Object owner) 
        throws HibernateException, SQLException { 
        Integer val = resultSet.getInt(names[0]); 
        E result = null; 
        if (!resultSet.wasNull()) {            
            for(E e : ****Class<E>****.values()) {
                if (e.ordinal() == val) {
                    return e;
                }
            }
        } 
        return result; 
    } 

How can we access .values()???

Any suggestion or solution?
Thanks

Benoit
 
Re: and a mapping to int in the DB? 15 Jul 2005, 20:41 benoitx
a template for mapping enum to int is available at
http://www.hibernate.org/312.html
 
Performance and memfootprint optimisation 17 Jul 2005, 21:08 KnisterPeter
For sake of performace this method should be slightly modified:
[code]
    public Object nullSafeGet(ResultSet resultSet, String[] names,
Object owner) throws HibernateException, SQLException { 
        String name = resultSet.getString(names[0]); 
        E result = null; 
        if (!resultSet.wasNull()) { 
            result = Enum.valueOf(clazz, name); 
        } 
        return result; 
    } 
[/code]
to
[code]
    public Object nullSafeGet(ResultSet resultSet, String[] names,
Object owner) throws HibernateException, SQLException { 
        E result = null; 
        if (!resultSet.wasNull()) { 
            String name = resultSet.getString(names[0]); 
            result = Enum.valueOf(clazz, name); 
        } 
        return result; 
    } 
[/code]
 
Re: Performance and memfootprint optimisation 28 Jul 2005, 05:46 sorenlarsen
POST QUESTIONS ON THE FORUM! COMMENTS HERE SHOULD ADD VALUE TO THE
PAGE!On 17 Jul 2005 21:08, KnisterPeter wrote:

>For sake of performace this method should be slightly modified:
>[code]
>    public Object nullSafeGet(ResultSet resultSet, String[] names,
>Object owner) throws HibernateException, SQLException {
>        String name = resultSet.getString(names[0]);
>        E result = null;
>        if (!resultSet.wasNull()) {
>            result = Enum.valueOf(clazz, name);
>        }
>        return result;
>    }
>[/code]
>to
>[code]
>    public Object nullSafeGet(ResultSet resultSet, String[] names,
>Object owner) throws HibernateException, SQLException {
>        E result = null;
>        if (!resultSet.wasNull()) {
>            String name = resultSet.getString(names[0]);
>            result = Enum.valueOf(clazz, name);
>        }
>        return result;
>    }
>[/code]

Actually, that won't work.  According to the API doc for
ResultSet.wasNull: "Note that you must first call one of the getter
methods on a column to try to read its value and then call the method
wasNull to see if the value read was SQL NULL."

Cheers,
Søren
 
missing bracket 15 Mar 2007, 15:45 jfrankman
A bracket is missing on the assemble method.

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

To:
public Object assemble(Serializable cached, Object owner) throws 
HibernateException  
{
         return cached;
}
 
Re: annnotations ? 11 Aug 2008, 03:53 thred
POST QUESTIONS ON THE FORUM! COMMENTS HERE SHOULD ADD VALUE TO THE
PAGE!On 24 Feb 2005 06:19, revoltingdigits wrote:

>hi there , any hints on how to get this working with annotations /
>is there going to be support for enums when working with annotations ?

>Thanks

>Bryan

If you are using JPA annotations  (at) Enumerated(EnumType (dot) STRING) should be
enought. No need for a usertype.
 
© Copyright 2006, Red Hat Middleware, LLC. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc. [Privacy Policy]