Technical blog

October 31, 2010

Using custom data type in Hibernate

Filed under: hibernate, java — Tags: , — paawak @ 19:06

Why need custom data type?

I have a java.util.Calendar object which I want to persist in the db, along with the TimeZone. Suppose I am working with a db which does not have support for storing TimeZone. How do I proceed? One of the easiest solutions would be to store the entire timestamp as a VARCHAR. Granted, but how do I instruct Hibernate to use a java.util.Calendar object instead of a String? Its done by annotating with @Type(type=”"), where type should be the fully qualified name of a class implementing the org.hibernate.usertype.UserType interface.

Custom UserType

public class TimeWithZone implements UserType {
 
    private static final Logger LOG = Logger.getLogger(TimeWithZone.class);
 
    /**
     * Define the supported column types
     */
    @Override
    public int[] sqlTypes() {
        return new int[] { Types.VARCHAR };
    }
 
    @Override
    public Class returnedClass() {
        return Calendar.class;
    }
 
    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
 
        if (x == null || y == null) {
            return false;
        }
 
        return x.equals(y);
    }
 
    @Override
    public int hashCode(Object x) throws HibernateException {
 
        if (x != null) {
            return x.hashCode();
        }
 
        return 0;
    }
 
    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
            throws HibernateException, SQLException {
 
        Calendar cal = null;
        String timestampStr = rs.getString(names[0]);
 
        if (timestampStr != null) {
            cal = getTimeWithZone(timestampStr);
        }
 
        return cal;
    }
 
    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index)
            throws HibernateException, SQLException {
 
        if (value == null) {
            st.setNull(index, Types.VARCHAR);
        } else {
 
            doInstanceCheck(value);
            Calendar cal = (Calendar) value;
            st.setString(index, getTimeWithZone(cal));
 
        }
 
    }
 
    @Override
    public Object deepCopy(Object value) throws HibernateException {
 
        Calendar clone = null;
 
        if (value != null) {
 
            doInstanceCheck(value);
            Calendar cal = (Calendar) value;
 
            // just copying the timezone and time
            clone = new GregorianCalendar();
            clone.setTimeInMillis(cal.getTimeInMillis());
            TimeZone tz = cal.getTimeZone();
            clone.setTimeZone(TimeZone.getTimeZone(tz.getID()));
        }
 
        return clone;
    }
 
    @Override
    public boolean isMutable() {
        return true;
    }
 
    @Override
    public Serializable disassemble(Object value) throws HibernateException {
 
        Calendar cal = null;
 
        if (value != null) {
 
            doInstanceCheck(value);
            cal = (Calendar) deepCopy(value);
 
        }
 
        return cal;
    }
 
    @Override
    public Object assemble(Serializable cached, Object owner)
            throws HibernateException {
        return disassemble(cached);
    }
 
    @Override
    public Object replace(Object original, Object target, Object owner)
            throws HibernateException {
        return disassemble(original);
    }
 
    protected void doInstanceCheck(Object value) {
 
        if ((value != null) && !(value instanceof Calendar)) {
            throw new UnsupportedOperationException(value.getClass()
                    + " not supported, expecting type "
                    + Calendar.class.getName());
        }
 
    }
 
    /**
     * Converts a String <em>2010-09-26 11:30:00 Australia/Adelaide</em> to
     * Calendar
     *
     * @param timeWithZone
     * @return
     */
    private Calendar getTimeWithZone(String timeWithZone) {
 
        String timeZoneId = timeWithZone.split("\\s")[2];
 
        TimeZone tz = TimeZone.getTimeZone(timeZoneId);
 
        String format = "yyyy-MM-dd HH:mm:ss";
        DateFormat df = new SimpleDateFormat(format);
        df.setTimeZone(tz);
 
        try {
            df.parse(timeWithZone);
        } catch (ParseException e) {
            LOG.error("could not parse date string: " + timeWithZone, e);
        }
 
        return df.getCalendar();
 
    }
 
    private String getTimeWithZone(Calendar timeWithZone) {
 
        String format = "yyyy-MM-dd HH:mm:ss zzzz";
        DateFormat df = new SimpleDateFormat(format);
        df.setTimeZone(timeWithZone.getTimeZone());
 
        return df.format(timeWithZone.getTime());
 
    }
 
}

Hibenate Entity

@Entity
@Table(name = "CUSTOM_DEMO")
public class CustomDemo {
 
    @Id
    @SequenceGenerator(name = "seq", allocationSize = 1, initialValue = 1, sequenceName = "CUSTOM_DEMO_SEQ")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
    private long id;
 
    @Column
    private String name;
 
    @Column(name = "TIME_WITH_ZONE")
    @Type(type = "com.swayam.demo.oracle.hibernate.TimeWithZone")
    private Calendar timeWithZone;
 
    public long getId() {
        return id;
    }
 
    public void setId(long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Calendar getTimeWithZone() {
        return timeWithZone;
    }
 
    public void setTimeWithZone(Calendar timeWithZone) {
        this.timeWithZone = timeWithZone;
    }
 
}

Resources

The sources can be found here.

Id generation in Hibernate with Sequence

Filed under: hibernate, java — Tags: , — paawak @ 18:47

I have the following SQL Script:

CREATE SEQUENCE  MY_SEQ
START WITH 1
INCREMENT BY 1;
 
CREATE TABLE  SEQ_DEMO (
	ID             INTEGER PRIMARY KEY NOT NULL,
	NAME     VARCHAR2(20) NOT NULL
    ) ;

The Hibernate entity for this would be:

@Entity
@Table(name = "SEQ_DEMO")
public class SeqDemo {
 
    @Id
    @SequenceGenerator(name = "seq", allocationSize = 1, initialValue = 1, sequenceName = "MY_SEQ")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
    private long id;
 
    @Column
    private String name;
 
    public long getId() {
        return id;
    }
 
    public void setId(long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
}

Powered by WordPress