DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Andrew trained as a biologist before transmuting into a software engineer. He's been coding in Java since 1997, and has done a bit of scientific software, a bit of mobile gaming and lots of enterprise software. He's particularly interested in humane and sustainable development. Professionally, that means code maintainability and agile methods. Outside the professional sphere: permaculture, gardening, cooking, green transport and raising children (not by order of importance). He also loves ensemble singing, but that’s on hold for now. Andrew is a DZone MVB and is not an employee of DZone and has posted 9 posts at DZone. You can read more from them at their website. View Full User Profile

Hibernate UserTypeSupport

12.15.2010
| 2641 views |
  • submit to reddit
        A base class for Hibernate UserTypes, that handles absolutely everything except the conversion between JDBC value and mapped entity value.

package net.andrewspencer.util.hibernate;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;

/**
 * <p>
 * Base class for Hibernate UserTypes.
 * </p>
 * <p>
 * This class handles all the mechanics except for the conversion itself
 * between JDBC and entity values, for which you provide an instance of JdbcEntityConverter.
 * 
 * @author Andrew Spencer
 */
public class UserTypeSupport<MappedClass, JdbcClass> implements UserType {

    /**
     * Conversion strategy between the value seen by JDBC and the representation
     * used in the entity class.
     * 
     * @param <MappedClass>
     *            The class of the property as declared in the entity
     * @param <JdbcClass>
     *            The class of the value returned by or passed to JDBC
     *            (JDBC must be able to convert between this class and
     *            the SQL type; the JDBC specs list the available conversions).
     * @author spencera
     */
    public interface JdbcEntityConverter<MappedClass, JdbcClass> {
        /**
         * Converts the JDBC value to the value mapped and exposed by the entity
         * 
         * @param value
         *            the value returned by JDBC (guaranteed non null)
         * @return the value to be set in the entity
         * @throws SQLException
         */
        MappedClass jdbcToEntity(JdbcClass databaseValue) throws SQLException;

        /**
         * @param objectValue
         *            the value present in the entity (guaranteed non null)
         * @return the value to pass to JDBC
         */
        Object entityToJdbc(MappedClass objectValue);
    }

    private Class<MappedClass> mappedClass = null;
    private Class<JdbcClass> jdbcClass = null;
    private int sqlType;
    private String dbTypeName = null;
    private JdbcEntityConverter<MappedClass, JdbcClass> jdbcEntityConverter;

    /**
     * @param mappedClass
     *            The class of the property defined on the entity
     * @param jdbcClass
     *            The class of the value passed to or returned from JDBC
     * @param sqlType
     *            The SQL type: one of the constants defined by java.sql.Types
     * @param jdbcEntityConverter
     *            The conversion strategy
     */
    protected UserTypeSupport(final Class<MappedClass> mappedClass, final Class<JdbcClass> jdbcClass,
            final int sqlType, final JdbcEntityConverter<MappedClass, JdbcClass> databaseRepresentation) {
        this.mappedClass = mappedClass;
        this.jdbcClass = jdbcClass;
        this.sqlType = sqlType;
        this.jdbcEntityConverter = databaseRepresentation;
    }

    public int[] sqlTypes() {
        return new int[] {sqlType};
    }

    public Class<MappedClass> returnedClass() {
        return mappedClass;
    }

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

        if (resultSet.wasNull()) {
            return null;
        }

        String columnName = names[0];
        int columnIndex = resultSet.findColumn(columnName);

        @SuppressWarnings("unchecked")
        // This overloading of getObject() ensures returned value is of correct type
        JdbcClass dbValue =
                (JdbcClass) resultSet.getObject(columnIndex, mapDbTypeNameToJdbcType(resultSet, columnIndex));
        // exception on preceding line? Ensure native SQL type is indeed convertible into <JdbcClass>

        return jdbcEntityConverter.jdbcToEntity(dbValue);
    }

    private Map<String, Class<?>> mapDbTypeNameToJdbcType(final ResultSet resultSet, int columnIndex)
            throws SQLException {

        Map<String, Class<?>> dbTypeNameToJdbcType = new HashMap<String, Class<?>>();

        // map only for the type we are interested in returning
        dbTypeNameToJdbcType.put(retrieveColumnTypeName(resultSet, columnIndex), jdbcClass);
        
        return dbTypeNameToJdbcType;
    }
    
    private synchronized String retrieveColumnTypeName(final ResultSet resultSet, final int columnIndex) throws SQLException {
        if (dbTypeName == null) {
            dbTypeName = resultSet.getMetaData().getColumnTypeName(columnIndex);
        }
        return dbTypeName;
    }

    public void nullSafeSet(final PreparedStatement preparedStatement, final Object value, final int index)
            throws HibernateException, SQLException {
        if (null == value) {
            preparedStatement.setNull(index, sqlType);
        } else {
            @SuppressWarnings("unchecked") // type must be <MappedClass> if Hibernate mapping is correct
            MappedClass valueCast = (MappedClass) value;
            preparedStatement.setObject(index, jdbcEntityConverter.entityToJdbc(valueCast), sqlType);
        }
    }

    public Object deepCopy(final Object value) throws HibernateException {
        return value;
    }

    public boolean isMutable() {
        return false;
    }

    public Object assemble(final Serializable cached, final Object owner) throws HibernateException {
        return cached;
    }

    public Serializable disassemble(final Object value) throws HibernateException {
        return (Serializable) value;
    }

    public Object replace(final Object original, final Object target, final Object owner)
            throws HibernateException {
        return original;
    }

    public int hashCode(final Object x) throws HibernateException {
        return x.hashCode();
    }

    public boolean equals(final Object x, final Object y) throws HibernateException {
        if (x == y) {
            return true;
        }
        if (null == x || null == y) {
            return false;
        }
        return x.equals(y);
    }
}