Search code examples
javahibernatejunitassertassertions

Why is assertEquals false if it is the same date? Hibernate


I'm generating one date and saving in a database through hibernate, and when I get the value and I compare with the value before it was inserted. The result is not equal!

I created the date as following

Date rightnow = Calendar.getInstance().getTime();

Task t1 = new Task("My task", rightnow);
taskDao.saveOrUpdate(t1);

Task taskR1 = taskDao.get(t1.getIdTask());
assertEquals("They should have to be equal dates",taskR1.getDate(),t1.getDate());

I'm getting this error

<2014-04-11 23:13:13.0> is different to <Fri Apr 11 23:13:13 CEST 2014>

java.lang.AssertionError:  
They should have to be equal dates  
expected:<2014-04-11 23:13:13.0>  
but was:<Fri Apr 11 23:13:13 CEST 2014>

Extra info related with the problem

Class Task

@Entity
@Table(name = "t_task")
public class Task {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "idTask")
    private long idTask;
    ...
    @Column(name = "date")
    private Date date;
    ...

Mysql table t_task

CREATE TABLE IF NOT EXISTS `mytask`.`t_task` (
  `idTask` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `date` DATETIME NOT NULL
  ...

I created a new hashCode() and equals() functions in Task, with only date field and even so it is different.

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((date == null) ? 0 : date.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (!(obj instanceof Task))
        return false;
    Task other = (Task) obj;
    if (date == null) {
        if (other.date != null)
            return false;
    } else if (!date.equals(other.date))
        return false;
    return true;
}

Any idea?


Solution

  • Sun's explanation, working with java client level (not with Hibernate), in the javadoc for java.sql.Timestamp, it states:

    Quote: public class Timestamp extends Date

    A thin wrapper around java.util.Date that allows the JDBC API to identify this as an SQL TIMESTAMP value. It adds the ability to hold the SQL TIMESTAMP nanos value and provides formatting and parsing operations to support the JDBC escape syntax for timestamp values.

    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.Date 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.

    @Test
    public void testTimestampVsDate() {
        java.util.Date date = new java.util.Date();
        java.util.Date stamp = new java.sql.Timestamp(date.getTime());
        assertTrue("date.equals(stamp)", date.equals(stamp));            //TRUE
        assertTrue("stamp.compareTo(date)", stamp.compareTo(date) == 0); //TRUE
        assertTrue("date.compareTo(stamp)", date.compareTo(stamp) == 0); //FALSE
        assertTrue("stamp.equals(date)", stamp.equals(date));            //FALSE
    }
    

    From javadoc we can figure out that:

    Timestamp = java.util.Date + nanoseconds

    and

    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.

    Timestamp compareTo() function

    public int compareTo(java.util.Date o) {
        if(o instanceof Timestamp) {
        // When Timestamp instance compare it with a Timestamp
        // Hence it is basically calling this.compareTo((Timestamp))o);
        // Note typecasting is safe because o is instance of Timestamp
        return compareTo((Timestamp)o);
        } else {
        // When Date doing a o.compareTo(this)
        // will give wrong results.
        Timestamp ts = new Timestamp(o.getTime());
        return this.compareTo(ts);
        }
    }