2011. november 10., csütörtök

Timestamp and Date equality when using Hibernate

Recently, I've come across an interesting issue when using Hibernate and Dates/Timestamps.

Suppose, you've got code like this:
public class TransferSchedule {
 
 @Id
 @GeneratedValue
 Integer id;

 @Column(name="start")
 @Temporal(TemporalType.TIMESTAMP)
 Date start;
 
.. other code omitted for clarity

}
Note: you may wander what is the use of @Column(name="start") when the variable is already named start. Actually this was needed to make the code less platform dependent, as mysql table names are case sensitive on Linux, but not on Windows..

So, you've got a List of TransferSchedules and you'd like to check if one of them starts on a user-supplied date value. You create a list of start dates and call contains:
if (listOfStartDates.contains(otherDate)) { .. }
The comparison can fail, even though you actually have the given date+time value in your list (according to Eclipse's debugger) The reason is - as I've found out in merely an hour of frustration -, that in case of a TIMESTAMP typed column, Hibernate will simply give you the java.sql.Timestamp supplied by the database driver. Normally, this causes no problem, as this class extends java.util.Date. However, when it comes to the equals method used by .contains(), they are not compatible. This is described in the respective Javadoc:

Note: This type is a composite of a java.util.Date and a separate nanoseconds value. Only integral seconds are stored in the java.util.Date component. The fractional seconds - the nanos - are separate. The Timestamp.equals(Object) method never returns true when passed a value of type java.util.Date because the nanos component of a date is unknown. As a result, the Timestamp.equals(Object) method is not symmetric with respect to the java.util.Date.equals(Object) method. Also, the hashcode method uses the underlying java.util.Data implementation and therefore does not include nanos in its computation. Due to the differences between the Timestamp class and the java.util.Date class mentioned above, it is recommended that code not view Timestamp values generically as an instance of java.util.Date. The inheritance relationship between Timestamp and java.util.Date really denotes implementation inheritance, and not type inheritance.

The easiest workaround to the problem is just to use the milisecond part when comparing Dates with Timestamps using the .getTime method.

Note, that you can also run into the same issue when using naked JDBC or Spring's JdbcTemplate/Rowmapper, etc.

Nincsenek megjegyzések:

Megjegyzés küldése