Tuesday, July 14, 2009

Hibernate Search by Date

On the project I am on now, we use Dates as a basis for monetary calculations. The Dates are simply based on the day, month and year, disregarding hours, minutes and seconds. We store our dates after base-lining them - stripping out the H:M:S. This is achieved with Calendar utilities:

static Date baseLineDate(Date myDate) {
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(myDate);
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}

Determining if a Date is before, after, or equal to another is straightforward thereafter- just use the Calendar before() and after() methods.

If you look in the database, the dates are stored as 01-Jan-2009. It would be tempting to simply run a Hibernate query that tests equality. You could simply generate a base-lined date and ask Hibernate to return the objects that have this Date. And it works.

However... Databases are part of the seedier side of the software town. Lurking there is data imported from other systems and/or updated by rogue administrators. The best way to handle all possible situations correctly is to use the Hibernate Criteria's between() method:


getCurrentSession().createCriteria( MyItem.class )
.add(Expression.between("myDateField", base-lined-Date, end-lined-Date))
.list();

Where the base-lined-Date has been base-lined as above, and the end-lined-Date corresponds to a Date that is the last millisecond of the day:

static Date EndLineDate(Date d){
if(d==null)return null;
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(d);
cal.set(Calendar.HOUR, 11);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.HOUR_OF_DAY,23);
cal.set(Calendar.MILLISECOND, 999);
return cal.getTime();
}

This solution pulls in all objects that have a certain Date defined by Day, Month and Year only.

No comments: