Mapping A Custom Type In Hibernate

The problem

I need to store an object that has a custom type as field.

Example

public class Something {

    ...

    private Kilos weight;

    ...
}

Where Kilos is

public class Kilos {
    private int weight;
}

The Kilos class is my value object, the one I want to save along with Something on db, and the one I want to define as custom type in hibernate.
In this example the Kilos class has just one field, so the table Something will have one column linked to the property ‘weight’.

The solution

Basically we want to have a green bar on this test:

@Test
public void shouldSaveAndLoadMyClass() throws Exception {
    Something something = new Something();
    something.setWeight(new Kilos(2));

    session.save(something);
    session.getTransaction().commit();

    Session anotherSession = HibernateUtil.getSessionFactory().openSession();
    Something somethingElse = (Something) anotherSession.createQuery("from Something").uniqueResult();
    HibernateUtil.getSessionFactory().close();

    assertEquals(something, somethingElse);
    assertEquals(2, somethingElse.getWeight().getNumber());
}

Ok then, lets map the Something class first…

Something.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    ...

    <class name="com.anv.tutorial.hibernate.customtypes.Something">
        <id name="id">
            <generator />;
        </id>
        <property name="weight" type="KilosType" />
    </class>

    ...

</hibernate-mapping>

As you can see there’s nothing strange in the mapping file but the attribute type in the tag property, which is telling us that the weight property is of type KilosType.

What is KilosType?

KilosType is our custom type that allows Hibernate to properly read and write any field of type Kilos.
A custom type must implement the interface org.hibernate.usertype.UserType.
This interface defines several methods, but two of them are the most important ones.
These methods are nullSafeSet() and nullSafeGet().
Hibernate uses these two methods to store and retrieve objects, and to map them on one (or more) column on the database.
KilosType has the two methods implemented as the followings:

public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
    throws HibernateException, SQLException {
    Integer number = (Integer) Hibernate.INTEGER.nullSafeGet(rs, names[0]);
    if (null == number) return null;
    return new Kilos(number);
}

public void nullSafeSet(PreparedStatement st, Object value, int index)
    throws HibernateException, SQLException {
    Kilos customField = (Kilos) value;
    if (null != customField) {
        int number = customField.getNumber();
        Hibernate.INTEGER.nullSafeSet(st, number, index);
    }
}

Mapping the custom type

Getting Hibernate to know what a Kilos is and how to map it is very easy. After having written the KilosType, what you have to do is to write a file where you define every custom type you want to use, just like the following example:

CustomTypes.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <typedef class="com.anv.tutorial.hibernate.customtypes.KilosType" name="KilosType" />
<hibernate-mapping>

The file CustomTypes.hbm.xml must be referenced, along with the Something.hbm.xml mentioned above, in hibernate.cfg.xml in this way:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>

    ...

    <mapping resource="CustomTypes.hbm.xml" />
    <mapping resource="com/anv/tutorial/hibernate/customtypes/Something.hbm.xml" />

    ...
</hibernate-configuration>

Ok, all the important matters have been covered. Beware that all the code above cannot be copied and pasted as is, since some parts have been omitted for clarity; well, that was my intention, at least :)

For the complete code download the attachment.

In the next days I’ll post how to map a custom type on multiple columns.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License