如果assertEquals是相同的日期,为什么这是假的?过冬

时间:2014-04-11 20:05:28

标签: java hibernate junit assert assertions

我正在通过休眠生成一个日期并保存在数据库中,当我得到该值并将其与插入之前的值进行比较。结果不相等!

我创建了以下日期

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());

我收到错误

<2014-04-11 23:13:13.0><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>

与问题相关的额外信息

课程任务

@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表t_task

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

我在Task中创建了一个新的hashCode()和equals()函数,只有日期字段,即使它是不同的。

@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;
}

有什么想法吗?

5 个答案:

答案 0 :(得分:8)

这是由java.sql.Timestamp混乱的设计引起的完全混乱,以及Hibernate返回此类的实例。实际上,您将java.util.Date实例存储到您的实体中。 Hibernate将其转换为java.sql.Timestamp以将其插入数据库中。但是当它从数据库中读取数据时,它不会将时间戳转换回java.util.Date。这很好,因为Timestamp扩展了Date。

但Timestamp永远不应该延长日期。实际上,Date精确到毫秒,而Timestamp精确到纳秒。为了能够比较两个Timestamp的纳秒部分,Timestamp会覆盖equals()方法,但会通过这样做来打破它的总体契约。最终结果是您可以date.equals(timestamp)为真,但timestamp.equals(date)为假。

我的建议:永远不要将日期实例与equals()进行比较。请改用compareTo()

答案 1 :(得分:2)

Sun在javadoc for java.sql.Timestamp中使用java客户端级别(不使用Hibernate)的解释说明:

  

<强>引用:   公共类Timestamp扩展日期

     

java.util.Date的一个瘦包装器,它允许JDBC API   将其标识为SQL TIMESTAMP值。它增加了持有的能力   SQL TIMESTAMP nanos值并提供格式化和解析   支持时间戳值的JDBC转义语法的操作。

     

注意:此类型是java.util.Date和单独的类型的组合   纳秒值。只有整数秒存储在   java.util.Date组件。分数秒 - 纳米 - 是   分离。 Timestamp.equals(Object)方法永远不会返回true   传递了java.util.Date类型的值,因为a的nanos组件   日期不详。因此,Timestamp.equals(Object)方法是   关于java.util.Date.equals(Object)不对称   方法。此外,hashcode方法使用底层的java.util.Date   实施,因此不包括纳米   计算。

     

由于Timestamp类和。之间的差异   上面提到的java.util.Date类,建议代码不行   查看时间戳值通常作为java.util.Date的实例。   Timestamp和java.util.Date之间的继承关系   真的表示实现继承,而不是类型继承。

@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
}

从javadoc我们可以看出:

  

时间戳= java.util.Date +纳秒

  

Timestamp.equals(Object)方法在传递a时永远不会返回true   java.util.Date类型的值,因为日期的nanos组件是   未知的。

时间戳compareTo()函数

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);
    }
}

答案 2 :(得分:1)

我建议您查看用于将日期存储在数据库中的类型。例如,Oracle DATE只能将精度降低到第二级,而TIMESTAMP可以像使用Java date那样达到毫秒级。

http://docs.oracle.com/cd/B19306_01/server.102/b14220/datatype.htm#CNCPT413

答案 3 :(得分:1)

对于那些正在寻找简单单元测试答案来比较日期的人,我使用SimpleDateFormatter将日期与Strings进行比较。这允许您在没有一堆数学的情况下指定您在比较中寻求的精度。

SimpleDateFormatter formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
assertEquals(formatter.format(someExpectedDate), formatter.format(someActualDate));

您可以修改格式以满足您的需求。

答案 4 :(得分:0)

这两个日期是不同的类(一个是java.util.Date,另一个是java.sql.Timestamp),所以它们不一样。

尝试此操作以检查日期值:   assertEquals(new Date(taskR1.getDate()。getTime()),t1.getDate());