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

Joe*_*Joe 7 java junit assert hibernate 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());
Run Code Online (Sandbox Code Playgroud)

我收到了这个错误

<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>
Run Code Online (Sandbox Code Playgroud)

与问题相关的额外信息

课程任务

@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;
    ...
Run Code Online (Sandbox Code Playgroud)

Mysql表t_task

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

我在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;
}
Run Code Online (Sandbox Code Playgroud)

任何的想法?

JB *_*zet 8

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

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

我的建议:永远不要将Date实例与equals().请compareTo()改用.


Joe*_*Joe 2

Sun 的解释,使用 java 客户端级别(而不是 Hibernate),在java.sql.Timestamp 的 javadoc中,它指出:

引用: 公共类 Timestamp 扩展了 Date

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

注意:此类型是 java.util.Date 和单独的纳秒值的组合。java.util.Date 组件中仅存储整数秒。秒的小数部分(纳秒)是分开的。当传递 java.util.Date 类型的值时,Timestamp.equals(Object) 方法永远不会返回 true,因为日期的 nanos 部分是未知的。因此,Timestamp.equals(Object) 方法与 java.util.Date.equals(Object) 方法不对称。此外,hashcode 方法使用底层 java.util.Date 实现,因此在其计算中不包括纳秒。

由于 Timestamp 类和上面提到的 java.util.Date 类之间的差异,建议代码不要将 Timestamp 值一般视为 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
}
Run Code Online (Sandbox Code Playgroud)

从javadoc我们可以看出:

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

和

当传递 java.util.Date 类型的值时,Timestamp.equals(Object) 方法永远不会返回 true,因为日期的 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);
    }
}
Run Code Online (Sandbox Code Playgroud)