Understanding and writing Hibernate user types

Discussions

News: Understanding and writing Hibernate user types

  1. Understanding and writing Hibernate user types (1 messages)

    In the careers of most Spring/Hibernate developers I know, there sooner or later comes a point of no escape...they have to write a Hibernate user type. The first one is usually of the copy'n'paste variety, and by and large works perfectly well. But when things are no longer going quite as expected - Hibernate is ignoring changes to items managed by the user type, for instance - it often becomes apparent that one doesn't sufficiently understand how these user type thingies are supposed to work. At least, that's what happened to me. In this post, we'll be dissecting the Hibernate UserType interface, explaining the relationships between the various methods, and developing a set of base user types that capture common use cases. Hibernate's UserType interface, in all it's glory (we'll be leaving the more complex EnhancedUserType to one side for the moment), looks like this: public interface UserType { public int[] sqlTypes(); public Class returnedClass(); public boolean equals(Object x, Object y) throws HibernateException; public int hashCode(Object x) throws HibernateException; public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException; public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException; public Object deepCopy(Object value) throws HibernateException; public boolean isMutable(); public Serializable disassemble(Object value) throws HibernateException; public Object assemble(Serializable cached, Object owner) throws HibernateException; public Object replace(Object original, Object target, Object owner) throws HibernateException; } Of the methods, returnedClass, nullSafeSet and nullSafeGet are probably the most self-explanatory. They are likely also the ones you tweaked for your first user type. Indeed, they are perhaps the only ones one would really want to have to deal with as a user type implementor, and making this (almost) possible is one of the main purposes of the base classes we will develop. What about the other methods, assemble, replace and the like? These used to be the ones whose implementation one copied from the example in semi-blind faith, although now that the Javadoc for them is much more detailed they will hopefully be more straightforward to implement. Still, the following "default" implementation is still common: @Override public boolean isMutable() { return false; } @Override public boolean equals(Object x, Object y) throws HibernateException { return ObjectUtils.equals(x, y); } @Override public int hashCode(Object x) throws HibernateException { assert (x != null); return x.hashCode(); } @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } What's wrong with this? Apart from the clutter, nothing, really...as long as you intend your objects to be treated as immutable. The following blog post describes problems that can arise with this implementation, and builds up base classes that allow you to focus, as much as possible, on only writing code relevant to the actual target type. You should end up with something like: public class ReadableStringBuilderUserType extends MutableUserType { public Class returnedClass() { return StringBuilder.class; } public int[] sqlTypes() { return new int[] { Types.VARCHAR }; } public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException { return nullSafeToStringBuilder(Hibernate.STRING.nullSafeGet(resultSet, names[0])); } private static StringBuilder nullSafeToStringBuilder(Object value) { return ((value != null) ? new StringBuilder(value.toString()) : null); } public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException, SQLException { Hibernate.STRING.nullSafeSet(preparedStatement, (value != null) ? value.toString() : null, index); } @Override public Object deepCopy(Object value) throws HibernateException { return nullSafeToStringBuilder(value); } }
  2. Is there any article around the Web explaining the performance issues that UserTypes might impact on huge data load through Hibernate? For me, it seems to be a overhead using a UserType for, let's say, PhoneNumber, instead of a simple String. I know systems that are full of UserTypes. There is not even one mapped classes using a plain Java class, like String, Integer or Date. Every column is mapped with a UserType class. I'd like to see what are the problems of doing this. Anyone out there with experience?