Ibatis and Timestamps (2)

People who have read my last post are now probably removing the Date classes from their beans, replacing the code with things as new Long(new Date.getTime()) and such. This is also what I started doing, until a collegue of mine pointed out that the TypeHandler I wrote last saturday could also do the trick. This would mean my beans could still contain clean Dates. I was so close, and failed to realize that I (or ibatis) had that power.

So I set out to do this and it works wonderfully. What I did was the following. I changes all postgres timestamp fields to bigints, like you read yesterday. I left my beans unchanged, containing java.util.Date objects for representing dates. So now all we need to do is write a typehandler which converts the date to a long and back, so Postgress and the buggy JDBC driver don't realize it's actually a Date.

To do this I wrote the following TypeHandler:

public class DateTypeHandler
implements TypeHandlerCallback {

public void setParameter(ParameterSetter setter,
Object parameter)
throws SQLException {
if (parameter == null) {
setter.setNull(Types.BIGINT);
} else {
Date datetime = (Date) parameter;
setter.setLong(datetime.getTime());
}
}

public Object getResult(ResultGetter getter)
throws SQLException {
long millis = getter.getLong();
if (getter.wasNull()) {
return null;
}
return new Date(millis);
}

public Object valueOf(String s) {
return s;
}
}

Now, all we need to do is tell ibatis to use this typehandler whenever we try to insert or retreive a java.util.Date. We do this by adding the following line to the ibatis SqlMapConfig.xml file, just after the typealias definitions:

<typeHandler javaType="java.util.Date"
callback="com.bestlaps.database.typehandlers.DateTypeHandler"/>

After doing this, you can cary on doing things as normal. Beans can contain java.util.Date, and ibatis happily inserts and retreives them in postgres bigint columns without any timezone conversion. Please note that timezones are not stored at all here, so when you retreive a Date from the database, the java.util.Date object will have your local JVM's devault timesone offset. You can happily ignore this, especially if you're not going to do any calculations with it.

The beauty of this solution is, that when there is a solution for the silly timezone conversion problem/bug, we can simply convert all database fields to timestamps, and remove the typehandler reference from the SqlMapConfig.xml file, and you're all set without changing a single line of code.

Thanks Joris!