Hibernate, java.util.Date and java.sql.Date Issues

Copied from The research Kitchen:

One issue that can crop up during unit testing is problems with date mapping. In SQL terms dates to a “day” level of precision can be specified using a DATE column and dates with a “time” element can be mapped using a TIMESTAMP (or MySQL DATETIME) column. These map respectively to the Java SQL types java.sql.Date and java.sql.Timestamp. Note that a java.util.Date object can specify times, up to a millisecond level of precision, whereas a java.sql.Timestamp object can handle nanosecond-level precision.

If we are using Hibernate to map a persistent class, and one of the fields is a day-level-precision Date field, we could map it like so:

In a unit test to exercise the persistence mapping, it could go something like the following:

Foo f = new Foo();
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
f.setStartDate(now);

and then test the retrieved persistent entity like so:

fooEntity = session.load(Foo.class, id);
assertEquals(f.getStartDate(), fooEntity.getStartDate());

The assertion will fail. The reason for this is that the call to fooEntity.getStartDate() returns an instance of java.sql.Date, whereas we are comparing against a java.util.Date. Since java.sql.Date is a subclass of java.util.Date, our tests will run without any ClassCastExceptions, but any equality checks will fail. The reason for this is apparent from the java.sql.Date javadoc:

To conform with the definition of SQL DATE, the millisecond values wrapped by a java.sql.Date instance must be ‘normalized’ by setting the hours, minutes, seconds, and milliseconds to zero in the particular time zone with which the instance is associated.

This means that any Date objects we retrieve from our Hibernate-mapped objects will have an effective time of midnight , i.e.something like 25-Jan-2006 00:00:00. We cannot just use the getTime() method to retrieve a long equaivalent of the date value either, as the they will ave the same problem.

What we can do is “normalize” the Date object before we persist it, like so:

private Date normalize(Calendar cal) {
cal.set(Calendar.MILLISECOND, 0);
cal.set(2006,0,25,0,0,0);
return cal.getTime();
}

...
Date now = normalize(cal);
event.setStartDate(now);

Now our asserts will pass without a problem.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s