Mapping enum to a table with hibernate annotation
Solution 1
Hibernate is kind of terrible at Enums. It's a strange failing of an otherwise pretty good ORM. The "easiest" way to get around it is to declare your Enum a custom hibernate type. Fortunately, Hibernate wrote an example implementation which you can crib verbatim into your app:
http://www.hibernate.org/265.html
They even include instructions on how to use it. This is the pattern I use whenever I end up with the need to persist enums.
Solution 2
I've created a similar class like the one suggested by hibernate only that is configurable and there is no need to create a new type only for this persistence.
Can be used like
@Type(type = "ro.raisercostin.hibernate.EnumUserType", parameters = @Parameter(name = "type", value = "DealType"))
DealType dealType;
I added an implementation of ParameterizedType to support the passed parameter.
public class EnumUserType implements UserType, ParameterizedType {
private static final int[] SQL_TYPES = { Types.VARCHAR };
private Class clazz = null;
public EnumUserType() {
}
@Override
public void setParameterValues(Properties parameters) {
String className = (String) parameters.get("type");
try {
this.clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Couldn't get the class for name [" + className + "].", e);
}
}
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, ((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);
}
}
Solution 3
You could annotate the enum with @Entity
and use a custoumn tuplizer to create the instances of the enum with Enum.valueOf
The enum declaration then looks like:
@Entity
@Table(name = "node_interface_type")
@Tuplizer(impl = EnumTuplizer.class)
public enum Type {
WIRED, WIRELESS, WIRELESS_SENSOR_NODE;
@Id
public String name = toString();
}
And the Tuplizer is:
public class EnumTuplizer extends PojoEntityTuplizer {
public EnumTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
super(entityMetamodel, mappedEntity);
}
@Override
protected Instantiator buildInstantiator(final PersistentClass persistentClass) {
return new Instantiator() {
@Override
public Object instantiate(Serializable id) {
try {
return Enum.valueOf(
(Class) persistentClass.getClass().getClassLoader().loadClass(persistentClass.getClassName()),
(String) id
);
} catch (ClassNotFoundException e) {
throw new AssertionError(e);
}
}
@Override
public Object instantiate() {
throw new UnsupportedOperationException();
}
@Override
public boolean isInstance(Object object) {
throw new UnsupportedOperationException();
}
};
}
}
Solution 4
If you want to use the entity just read-only, then you can use @Formula
and @Enumerated
. Try something like:
@Entity
public class Deal {
@Formula("(select text from DEAL_TYPE dt where dt.id = typeId)")
@Enumerated(EnumType.STRING)
DealType type;
}
Solution 5
Although far far from ideal, my solution to this problem was to use EnumStringType and a denormalized updatable view.
Comments
-
Thierry Roy almost 4 years
I have a table DEAL and a table DEAL_TYPE. I would like to map this code:
public class Deal { DealType type; } public enum DealType { BASE("Base"), EXTRA("Extra"); }
The problem is that the data already exist in the database. And I'm having a hard time mapping the classes to the database.
The database looks something like that:
TABLE DEAL { Long id; Long typeId; } TABLE DEAL_TYPE { Long id; String text; }
I know I could use a simple @OneToMany relationship from deal to deal type, but I would prefer to use an enum. Is this possible?
I almost got it working by using a EnumType.ORDINAL type. But unfortunately, my IDs in my deal type table are not sequential, and do not start at 1.
Any suggestions?
-
Peter Becker over 12 yearsIt seems Hibernate expanded the signature on two methods, but otherwise this works fine for me. Ever considered contributing it to the Hibernate project?
-
Gepsens over 12 yearsI think you can now get rid of the Tuplizer with @Enumerated
-
Naor about 12 yearsBut where do you define the id column and the text column of the enum?
-
xmedeko almost 12 yearsNot a bad solution, since you have considered insert and update, too. Some other solutions here are probably just for reading. Although it is possible only for few DB's, like Oracle.
-
Jeff Mc over 11 yearsWhile I implemented this on MSSQL where updatable views are extremely simple, it should be possible to emulate on most RDBMS systems by placing an 'instead of' trigger on the view to modify the underlying tables.
-
yousafsajjad over 10 years@raisercostin, I am trying what you have explained above. I am not sure what would be the parameter values in the annotation. Can you please elaborate on it or point to the source where I can get more information on it.
-
yousafsajjad over 10 yearsNow I understand. The parameters are like type: DealType where type is the key used EnumUserType and value is the class (use complete class name including package).
-
t0tec over 9 yearsI don't understand how you map the Enum to a separate table with id and description. Could you give a code example how to to this in Hibernate annotations? It's what @Naor has asked.
-
Andrii Plotnikov almost 8 yearsyou're life saver. With this I can instatiate hibernate objects as enums with custom values and use them in repositories
-
Erwan Leroux about 4 yearsThis answer is outdated, we can now use docs.oracle.com/javaee/7/api/javax/persistence/Enumerated.html