We persist enums as strings. Use @Enumerated(EnumType.STRING)
to use the string representation (instead of the automatic enum code). That makes your data in the DB much more readable.
If you really need to map the enums to special codes (legacy code and the like), you'll need a custom mapping. First a base class which maps enums to the DB and back:
import java.io.Serializable;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
public abstract class CustomEnumType implements UserType
{
public Object deepCopy (Object value) throws HibernateException
{
return value;
}
public Serializable disassemble (Object value) throws HibernateException
{
return (Serializable)value;
}
public Object assemble (Serializable cached, Object owner)
throws HibernateException
{
return cached;
}
public boolean equals (Object x, Object y) throws HibernateException
{
// Correct for Enums
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 int[] sqlTypes ()
{
return new int[]{ Hibernate.STRING.sqlType() };
}
}
Now an extension which uses DBEnum to map values for your enums to the DB and back:
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.apache.log4j.Logger;
public abstract class DBEnumType extends CustomEnumType
{
private final static Logger log = Logger.getLogger(DBEnumType.class);
private static final boolean IS_VALUE_TRACING_ENABLED = log.isTraceEnabled();
public Object nullSafeGet (ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException
{
String value = rs.getString (names[0]);
if (rs.wasNull ())
return null;
return toEnum (value);
}
public abstract Object toEnum (String value);
public void nullSafeSet (PreparedStatement st, Object value, int index)
throws HibernateException, SQLException
{
if (value == null)
st.setNull (index, Hibernate.STRING.sqlType ());
else
{
DBEnum dbEnum = (DBEnum)value;
value = dbEnum.getDBCode();
st.setString (index, dbEnum.getDBCode());
}
if (IS_VALUE_TRACING_ENABLED)
{
log.trace (getClass().getName()+" "+value);
}
}
}
The interface:
public interface DBEnum
{
public String getDBCode ();
}
Lastly, you must extend DBEnumType
for each enum type you want to map:
public class DBEnumCardType extends DBEnumType
{
public Class returnedClass ()
{
return Status.class;
}
public Object toEnum (String value)
{
return Status.fromDBCode (value);
}
}
In Status
, you must implement the static method which maps DB codes to enums:
private final static Map<String, Status> dbCode2Enum = new HashMap<String, Status> ();
static {
for (Status enm: Status.values ())
{
String key = enm.getDBCode ();
if (dbCode2Enum.containsKey (key))
throw new ShouldNotHappenException ("Duplicate key "+key+" in "+enm.getClass ());
dbCode2Enum.put (key, enm);
}
}
private String dbCode;
private Status (String dbCode)
{
this.dbCode = dbCode;
}
public String getDBCode ()
{
return dbCode;
}
public static Status fromDBCode (String dbCode)
{
if (dbCode == null)
return null;
Status result = dbCode2Enum.get (dbCode);
if (result == null)
throw new ShouldNotHappenException ("Can't find key "+dbCode+" in "+Status.class);
return result;
}
Lastly, you must use the annotation @org.hibernate.annotations.Type()
to tell Hibernate to use the custom mapping.
Conclusion: Don't use custom codes. They just generate a lot of stupid boiler plate code which you can't factor out.