2014-04-11 17 views
6

Ich erzeuge ein Datum und speichern in einer Datenbank durch den Ruhezustand, und wenn ich den Wert bekomme und ich vergleiche mit dem Wert, bevor es eingefügt wurde. Das Ergebnis ist nicht gleich!Warum ist assertEquals falsch, wenn es das gleiche Datum ist? Hibernate

habe ich das Datum wie folgt

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

ich diesesFehler immer mit t

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

Zusätzliche Informationen im Zusammenhang er Problem

Klasse Aufgabe

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

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

habe ich ein neues hashCode() und equals() Funktionen in Aufgabe, mit nur Datumsfeld und sogar so es ist anders.

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

Irgendeine Idee?

+1

: 55 vs: 54?Sieht für mich nicht gleich aus - meine anfängliche Vermutung wäre, dass die Datenbank den Wert selbst setzt. – user2864740

+2

Sind diese die gleichen 'Date' Typ? Sie sehen so aus, als hätten sie verschiedene "toString" -Formate. –

+0

Ich habe eine andere Ausführung und den HashCode und gleiche Funktionen hinzugefügt, ich weiß nicht, was ich noch tun soll? – Joe

Antwort

2

Suns Erklärung, mit Java-Client-Ebene arbeiten (nicht mit Hibernate), in der javadoc for java.sql.Timestamp, heißt es:

Zitat: public class Zeitstempel erweitert Java Datum

Eine dünne Hülle um .util.Date, mit dem die JDBC-API dies als SQL-TIMESTAMP-Wert identifizieren kann. Es fügt die Fähigkeit hinzu, den SQL TIMESTAMP nanos-Wert zu enthalten, und stellt -Operationen zum Formatieren und Analysieren bereit, um die JDBC-Escape-Syntax für Zeitstempelwerte zu unterstützen.

Hinweis: Dieser Typ ist eine Zusammensetzung aus einem java.util.Date und einem separaten Nanosekunden-Wert. In der Komponente java.util.Date werden nur ganzzahlige Sekunden gespeichert. Die Sekundenbruchteile - die Nanos - sind getrennt. Die Methode "Timestamp.equals (Object)" gibt nie den Wert "true" zurück, wenn einen Wert vom Typ "java.util.Date" übergeben wurde, weil die Nanoskomponente eines Datums unbekannt ist. Daher ist die Timestamp.equals (Object) -Methode nicht symmetrisch in Bezug auf die Methode java.util.Date.equals (Object) . Außerdem verwendet die Hashcode-Methode die zugrunde liegende Implementierung java.util.Date und enthält daher in ihrer Berechnung keine Nanos.

Aufgrund der Unterschiede zwischen der Zeitstempel-Klasse und der oben genannten java.util.Date Klasse, wird es diesen Code nicht Ansicht Timestamp-Werte allgemein als eine Instanz von java.util.Date empfohlen. Die Vererbungsbeziehung zwischen Timestamp und java.util.Date bedeutet wirklich Implementierungsvererbung und keine Typvererbung.

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

Von javadoc können wir herausfinden, dass:

Timestamp = java.util.Date + ns

und

Der Zeitstempel. Gleich (Objekt) Methode neve r gibt true zurück, wenn ein Wert vom Typ java.util.Date übergeben wurde, da die Nanokomponente eines Datums unbekannt ist.

Timestamp compareTo() Funktion

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

Ich würde vorschlagen, Sie schauen, welchen Typ Sie verwenden, um das Datum in der Datenbank zu speichern. Zum Beispiel hat ein Oracle DATE nur eine Genauigkeit bis auf die zweite Ebene, während TIMESTAMP genau wie bei einem Java-Datum eine Millisekunde haben kann.

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

+0

In Java verwende ich Date und in MySQL ein DATETIME. Und ich habe auch die Annotation @Temporal (TemporalType.TIMESTAMP) aber auch .DATE und TIME versucht. Keiner von ihnen funktioniert :( – Joe

0

Die beiden Termine sind von verschiedenen Klassen (eine davon ist ein java.util.Date, der andere java.sql.Timestamp ist), so sind sie nicht gleich.

Versuchen Sie dies, um die Datumswerte zu überprüfen: assertEquals (neues Datum (taskR1.getDate(). GetTime()), t1.getDate());

8

Dies ist eine komplette Unordnung, die durch das java.sql.Timestamp durcheinandergebrannte Design verursacht wird, und durch Hibernate, die Instanzen dieser Klasse zurückgibt. Tatsächlich speichern Sie eine java.util.Date Instanz in Ihrer Entität. Hibernate transformiert das in eine java.sql.Timestamp, um es in die Datenbank einzufügen. Aber wenn es die Daten aus der Datenbank liest, wird der Zeitstempel nicht in eine java.util.Date zurückversetzt. Das funktioniert gut, weil Timestamp Date verlängert.

Aber Timestamp sollte nie erweitertes Datum haben. Tatsächlich ist Date bis zur Millisekunde genau, wohingegen Timestamp bis in die Nanosekunde genau ist. Um die Nanosekunden-Teile von zwei Timestamps vergleichen zu können, setzt Timestamp die equals() -Methode außer Kraft, bricht jedoch dadurch seinen allgemeinen Vertrag auf. Das Endergebnis ist, dass date.equals(timestamp) wahr sein kann, aber timestamp.equals(date) falsch ist.

Mein Tipp: Vergleichen Sie niemals Datumsinstanzen mit equals(). Verwenden Sie stattdessen compareTo().

+0

Überprüfen Sie dies mit MongoDB und java.time.LocalDateTime, und das Millisekunde-Nanosekunden-Problem existiert auch dort. Trimmen das Datum bei der Einstellung mit LocalDateTime löst das Problem. – cst1992

+0

Ich habe das gleiche Problem, aber Ich verwende mybatis. Haben sie den gleichen Grund? Siehe http://stackoverflow.com/questions/38722942/why-mybatis-insert-java-util-date-value-to-mysql-datetime-column-then-fetch -es-t – zhuguowei

0

Für diejenigen, die für eine einfache Unit-Tests Antwort auf den Vergleich Daten suchen, habe ich eine SimpleDateFormatter vergleichen Daten als Strings verwendet. Auf diese Weise können Sie die Genauigkeit angeben, die Sie im Vergleich ohne mathematische Berechnungen suchen.

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

Sie können das Format an Ihre Bedürfnisse anpassen.