UserType for persisting a Typesafe Enumeration with a VARCHAR column

Use this Typesafe Enumeration class in your domain model as a simple property type (e.g. of the Comment class in a forum):

public class Rating implements Serializable {

    private String name;

    public static final Rating EXCELLENT = new Rating("Excellent");
    public static final Rating OK = new Rating("OK");
    public static final Rating LOW = new Rating("Low");
    private static final Map INSTANCES = new HashMap();

    static {
        INSTANCES.put(EXCELLENT.toString(), EXCELLENT);
        INSTANCES.put(OK.toString(), OK);
        INSTANCES.put(LOW.toString(), LOW);
    }

    private Rating(String name) {
        this.name=name;
    }

    public String toString() {
        return name;
    }

    private Object readResolve() {
        return getInstance(name);
    }

    public static Rating getInstance(String name) {
        return (Rating)INSTANCES.get(name);
    }
}

 

Next, write the Hibernate custom mapping type:

public class RatingUserType implements UserType {

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

    public int[] sqlTypes() { return SQL_TYPES; }
    public Class returnedClass() { return Rating.class; }
    public boolean equals(Object x, Object y) { return x == y; }
    public Object deepCopy(Object value) { return value; }
    public boolean isMutable() { return false; }

    public Object nullSafeGet(ResultSet resultSet,
                              String[] names,
                              Object owner)
            throws HibernateException, SQLException {

      String name = resultSet.getString(names[0]);
      return resultSet.wasNull() ? null : Rating.getInstance(name);
    }

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

        if (value == null) {
            statement.setNull(index, Types.VARCHAR);
        } else {
            statement.setString(index, value.toString());
        }
    }
}

 

Finally, in your mapping files, bind it all together:

<hibernate-mapping package="org.hibernate.auction.model">

<class name="Comment"
       table="COMMENTS"
       lazy="true">

    <!-- Common id property. -->
    <id name="id"
        type="long"
        column="COMMENT_ID"
        unsaved-value="null">
        <generator class="native"/>
    </id>

    <!-- Simple property. -->
    <property
        name="rating"
        column="RATING"
        type="org.hibernate.auction.persistence.RatingUserType"
        not-null="true"
        update="false"/>
...
</class>
</hibernate-mapping>

 

You may also use your Rating enumerated type in Hibernate queries:

Query q = session.createQuery("from Comment c where c.rating = :rating");
q.setParameter("rating",
               Rating.LOW,
               Hibernate.custom(RatingUserType.class));

 

This is all there is to know about it. You may enhance this strategy with a generic UserType implementation, that knows how to persist different Typesafe Enumerations, this is described here.

The examples and domain model you have just seen can be found in Hibernate in Action.

Christian