Hibernate is used to map java objects to a relational database. Ideally, you would like your java object model to be as object-oriented as possible. It is also nice to be able to use features like java 5 enums while coding the object model. In this tutorial, I’ll share with you how I ended up mapping a simple object model using java 5 enums into Hibernate.
A Simple Example
I have a Beer object that corresponds directly to a BEER table in a database. The Beer object can wither be an “Ale” or a “Lager”, and there may be more types in the future. So the database schema looks like this:
+--------------------+ | BEER | +--------------------+ +----------------+ | NUMBER | ID | | BEER_TYPE | | VARCHAR | BRAND | +----------------+ | NUMBER | TYPE | FK------- | NUMBER | ID | | NUMBER | VOLUME | | VARCHAR | NAME | +--------------------+ +----------------+
It is pretty straightforward how to create a java class to represent this data. Ideally, I would like to use a java 5 enum object to keep track of the beer type, so I use it within my data object to specify type:
public class Beer {
private BeerType type;
private String brand;
private double volume;
public BeerType getType() {
return type;
}
public void setType(BeerType type) {
this.type = type;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getVolume() {
return volume;
}
public void setVolume(double volume) {
this.volume = volume;
}
}
The BeerType enum will simply specify the type of beer for each Beer object. This enum is going to map directly to the BEER_TYPE table. So I’ve given each beer type its own id through the private internal enum constructor. There is an accessor method for hibernate to retrieve the id when it needs to do the mapping.
UNKNOWN(0), ALE(1), LAGER(2);
private int id;
private BeerType(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
One of the first things I found when I started searching the internet for ways to map Java 5 enums to a database using Hibernate was this article on the Hibernate page. It shows an implementation of the Hibernate UserType in a way that will allow an enum to be used within a hibernate key mapping. So if I create a custom type definition in the key-mapping that uses the GenericEnumUserType class as the class, and passes in the actual BeerType enum classpath as a parameter, I can do the work to break out the enum details in the GenericEnumUserType implementation.
<hibernate-mapping>
<typedef class="net.dangertree.GenericEnumUserType" name="beerType">
<param name="enumClassName">net.dangertree.BeerType</param>
<param name="identifierMethod">getId</param>
</typedef>
<class name="net.dangertree.Beer" table="BEER">
<id name="id" type="int" column="ID">
<generator class="native"/>
</id>
<property name="type" type="beerType" column="TYPE"/>
<property name="brand" type="string" column="BRAND"/>
<property name="volumn" type="double" column="VOLUME"/>
</class>
</hibernate-mapping>
Here is the full code for the GenericEnumUserType class that I used to help map my enums into Hibernate. Reflection is used to find an “identifier” method in the enum that will get the id for each value. I have specified in the type-def in my key mapping that I’m going to use the getId() method of my enum to get an identifying attribute. By default, the class will look for the inherent name method of enum, but I’m using ids as my identifier.
The two method objects are grabbed and saved, then called in the null-safe setter and getter deeper within the Hibernate workings.
import org.hibernate.type.NullableType;
import org.hibernate.type.TypeFactory;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
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;
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 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(â€enumClassNameâ€);
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;
}
}
Ok, there is only one problem now. The DEFAULT_VALUE_OF_METHOD_NAME method is specified by default to be valueOf, and I have not provided a parameter to override that in the type-def. So in order for my enum to match this, I need to add a valueOf method to my enum. And when the nullSafeGet method calls the “valueOfMethod“, it sends it an identifier as a parameter, so our valueOf method needs to take an int id as a parameter.
UNKNOWN(0), ALE(1), LAGER(2);
private int id;
private BeerType(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static BeerType valueOf(int id) {
switch (id) {
case 1: return ALE;
case 2: return LAGER;
default: return UNKNOWN;
}
}
}
Now I have my data object, the enum it is using, my key-mapping, and an implementation of UserType that provides a way to access the enum through a type-def element in the key-mapping.
It’s all working for me now! Does anyone have any questions or comments? This is the very first time that I have interacted with hibernate, so I’m a little bit shaky with the details (especially the UserType implementation).
Edit: correction of mapping file pointed out by commenters.

21 Comments
I think @Enumerated does the same thing. This is a JPA annotation - u can use the xml equivalent as well. And Hibernate supports it very well.
@Enumerated(EnumType.STRING)
public BeerType getBeerType() {
//..
}
The data object that I’m trying to persist is in a common directory, and “@Enumerated” is a Hibernate annotation. Because I don’t want to add Hibernate dependencies to my common packages, I don’t think I can do this.
I did not know about the @Enumerated annotation, however, so thank you for the tip!
@Enumerated is a JPA annotation (import javax.persistence.Enumerated;) and does not induce any Hibernate dependency. And the custom data type that u r trying to implement is very much Hibernate specific.
I am going to look further into that and I’ll report my results back.
There is an error in your config file.
should be
net.dangertree.BeerType
please update it, thanks.
@zigzag
I don’t think there is an error. Where do you mean? The only place where I’ll use “net.dangertree.BeerType” is as a parameter to GenericEnumUserType.
How come you are not (re)using Enum.ordinal() instead of the hardcoded ids?
Could the problem be here:
sorry here : ame=”net.dangertree.BeerType
I have a question about this - how does Hibernate know the name of the table to use when persisting BeerTypes?
Oh - I suppose it doesn’t need to know because all the data is encapsulated in the class itself.
Actually, if you look at the Hibernate key mapping above, you’ll see table=”BEER”. So there you go.
That’s the table for type Beer, not for type BeerType. From this implementation it looks like Hibernate wouldn’t know anything about the BEER_TYPE table.
I gotcha. But note that in this model, no data is ever persisted in or retrieved from the BEER_TYPE table. It is just there for the schema to be complete. Our BeerType enum is just a mirror of it for the Java side. When a Beer object is read or written, the BEER_TYPE table is never touched, so no mapping is required.
However, it is still necessary so the same image exists on the database and in the persistence layer of Java code. If you want to add a new “STOUT” type of beer, you would have to update the DB schema and the BeerType enum.
Sorry for the confusion in my earlier comment. I was 1/2 asleep this morning :(.
Nice! Though hard Coding the CASE is a pain, I like to embed more data in the Enum like as follows
public enum InternalErrorCode { STATUS_NOERROR(0, "NO ERRORS"), STATUS_INVALID_PARAM(1020, "Invalid parameter type or length"); private final int statusCode; private final String statusDesc; InternalErrorCode(int code, String desc) { this.statusCode = code; this.statusDesc = desc; } public int getValue() { return statusCode; } public String getDesc() { return statusDesc; } public static InternalErrorCode valueOf(int id) { InternalErrorCode[] list = InternalErrorCode.values(); for( InternalErrorCode enumItem : list ) { if ( enumItem.getValue() == id ){ return enumItem; } } return null; } public String toString() { return new StringBuilder() .append(statusCode).append(" - ").append(statusDesc).toString(); }As another user has pointed out, your hibernate mapping file is wrong.
It should be:
net.dangertree.BeerType
Also, it won’t create a beer type table. It will set the column in the beer table to an enumerated type, your beer type table won’t get created or populated via hibernate.
Arg…
I can’t believe the code tag doesn’t work here…..
Your mapping file should be
&l;ttypedef class=”net.dangertree.GenericEnumUserType” name=”beerType”>
<param name=”enumClassName”>net.dangertree.BeerType</param>
One more time with feeling….
<typedef class=”net.dangertree.GenericEnumUserType” name=”beerType”>
<param name=”enumClassName”>net.dangertree.BeerType</param>
@Rick Fisk
I’ve fixed the error you’ve pointed out. Thanks for spelling it out for me.
You said “Also, it won’t create a beer type table. It will set the column in the beer table to an enumerated type, your beer type table won’t get created or populated via hibernate.”
I want to make it clear that this type of mapping cannot be used to update or insert into the BEER_TYPE table, and in fact is never actually mapped to that table. The only thing it is used for is to find the correct BEER_TYPE FK value from the BeerType enum to be inserted into the BEER table.
If either the BeerType enum or the BEER_TYPE table are changed, the opposite entity must also be changed. This all assumes that the existence of an enumeration in the first place has something to do with the application logic, and won’t be normally updated without significant logic changes within the program.
Also, I’ve never used Hibernate to create tables, and I didn’t know it had the capability to run any DDL.
Find below another article with the internationalization support.
Java Enumerations, internationalization and usage in Hibernate
How do you do a named query with an enumerated type?
from MyTable where myEnum = ???????
the id? myEnum.id? the value???
2 Trackbacks
[...] http://weblog.dangertree.net/2007/09/23/mapping-java-5-enums-with-hibernate/ [...]
[...] z wujkiem G zaowocowa?a dwoma linkami – blogiem dangertree, a tak?e znajduj?cym si? tam odno?nikiem do oficjalnej dokumentacji Hibernate. Rzecz jest [...]