Antworten:
Herzlichen Glückwunsch, Sie haben mit JDBC meinen Lieblings-Pet-Peeve getroffen: Umgang mit Datumsklassen.
Grundsätzlich unterstützen Datenbanken normalerweise mindestens drei Arten von Datums- / Uhrzeitfeldern: Datum, Uhrzeit und Zeitstempel. Jeder von ihnen hat eine entsprechende Klasse in JDBC und jeder von ihnen wird erweitertjava.util.Date
. Die schnelle Semantik jeder dieser drei ist die folgende:
java.sql.Date
entspricht SQL DATE, dh es werden Jahre, Monate und Tage gespeichert, während Stunde, Minute, Sekunde und Millisekunde ignoriert werden. Darüber hinaus sql.Date
ist nicht an Zeitzonen gebunden.java.sql.Time
entspricht SQL TIME und enthält, wie offensichtlich sein sollte, nur Informationen zu Stunden, Minuten, Sekunden und Millisekunden .java.sql.Timestamp
entspricht SQL TIMESTAMP, dem genauen Datum der Nanosekunde ( beachten Sie, dass util.Date
nur Millisekunden unterstützt werden! ) mit anpassbarer Genauigkeit.Einer der häufigsten Fehler bei der Verwendung von JDBC-Treibern in Bezug auf diese drei Typen ist, dass die Typen falsch behandelt werden. Dies bedeutet, dass dies sql.Date
zeitzonenspezifisch ist und sql.Time
das aktuelle Jahr, den aktuellen Monat und den aktuellen Tag usw. enthält.
Kommt wirklich auf den SQL-Typ des Feldes an. PreparedStatement
hat Setter für alle drei Werte, #setDate()
die für sql.Date
, #setTime()
für sql.Time
und #setTimestamp()
für sql.Timestamp
.
Beachten Sie, dass Sie bei Verwendung der meisten JDBC-Treibern ps.setObject(fieldIndex, utilDateObject);
tatsächlich einen normalen util.Date
Wert geben können, der ihn gerne verschlingt, als wäre er vom richtigen Typ. Wenn Sie die Daten anschließend anfordern, werden Sie möglicherweise feststellen, dass Ihnen tatsächlich etwas fehlt.
Was ich damit sagen möchte , dass Sie die Millisekunden / Nanosekunden als einfache Longs speichern und sie in die von Ihnen verwendeten Objekte konvertieren ( obligatorischer Joda-Time-Plug ). Eine hackige Möglichkeit, die durchgeführt werden kann, besteht darin, die Datumskomponente als eine lange und eine Zeitkomponente als eine andere zu speichern. Diese lauten derzeit beispielsweise 20100221 und 154536123. Diese magischen Zahlen können in SQL-Abfragen verwendet werden und können von der Datenbank in eine andere und portiert werden Damit können Sie diesen Teil der JDBC / Java-Datums-API: s vollständig vermeiden.
LATE EDIT: Ab Java 8 sollten Sie weder verwenden java.util.Date
noch java.sql.Date
wenn Sie es überhaupt vermeiden können, und stattdessen lieber das java.time
Paket (basierend auf Joda) als irgendetwas anderes verwenden. Wenn Sie nicht mit Java 8 arbeiten, finden Sie hier die ursprüngliche Antwort:
java.sql.Date
- wenn Sie Methoden / Konstruktoren von Bibliotheken aufrufen, die sie verwenden (wie JDBC). Nicht anders. Sie möchten keine Abhängigkeiten in die Datenbankbibliotheken für Anwendungen / Module einführen, die sich nicht explizit mit JDBC befassen.
java.util.Date
- bei Verwendung von Bibliotheken, die es verwenden. Ansonsten so wenig wie möglich aus mehreren Gründen:
Es ist veränderlich, was bedeutet, dass Sie jedes Mal, wenn Sie es an eine Methode übergeben oder von einer Methode zurückgeben, eine defensive Kopie davon erstellen müssen.
Es geht nicht sehr gut mit Datumsangaben um, was Leute wie Ihre wirklich rückwärts denken, dass Datumsbehandlungsklassen dies tun sollten.
Nun, weil juD seine Arbeit nicht sehr gut macht, wurden die schrecklichen Calendar
Klassen eingeführt. Sie sind auch veränderlich und schrecklich zu bearbeiten und sollten vermieden werden, wenn Sie keine Wahl haben.
Es gibt bessere Alternativen, wie die Joda Time API ( die es möglicherweise sogar in Java 7 schafft und zur neuen offiziellen API für die Datumsverarbeitung wird - eine schnelle Suche sagt, dass dies nicht der Fall ist).
Wenn Sie der Meinung sind, dass es übertrieben ist, eine neue Abhängigkeit wie Joda einzuführen, ist long
es gar nicht so schlecht, sie für Zeitstempelfelder in Objekten zu verwenden, obwohl ich sie normalerweise selbst in juD einwickle, wenn ich sie weitergebe, zur Typensicherheit und als Dokumentation.
java.sql.Date
mit PreparedStatement
etc! Wenn Sie es jedoch weitergeben, verwenden Sie ein, LocalDate
das Sie mit java.sql.Date.valueOf
und java.sql.Date.valueOf
beim Festlegen konvertieren, und konvertieren Sie es so früh wie möglich mit java.sql.Date.toLocalDate
- wieder, weil Sie java.sql so wenig wie möglich einbeziehen möchten und weil es veränderbar ist.
Die einzige Zeit zu verwenden java.sql.Date
ist in a PreparedStatement.setDate
. Andernfalls verwenden Sie java.util.Date
. Es ist bezeichnend, dass ResultSet.getDate
a zurückgegeben wird, java.sql.Date
aber es kann direkt a zugewiesen werden java.util.Date
.
java.sql.Date
kann, java.util.Date
wenn das erstere das letztere erweitert? Was ist der Punkt, den Sie versuchen zu machen?
Ich hatte das gleiche Problem. Der einfachste Weg, das aktuelle Datum in eine vorbereitete Erklärung einzufügen, ist folgender:
preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Verwenden Sie keine.
java.time.Instant
ersetzt java.util.Date
java.time.LocalDate
ersetzt java.sql.Date
java.util.Date vs java.sql.Date: wann und warum?
Beide Klassen sind schrecklich, fehlerhaft im Design und in der Implementierung. Vermeiden Sie wie das Pest- Coronavirus .
Verwenden Sie stattdessen java.time- Klassen, die in JSR 310 definiert sind. Diese Klassen sind ein branchenführendes Framework für die Arbeit mit der Datums- und Uhrzeitbehandlung. Dieser supplant völlig die blutigen schreckliches Erbe Klassen wie Date
, Calendar
, SimpleDateFormat
, und so weiter .
java.util.Date
Der erste java.util.Date
soll einen Moment in UTC darstellen, dh einen Versatz von UTC von null Stunden-Minuten-Sekunden.
java.time.Instant
Jetzt ersetzt durch java.time.Instant
.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
java.time.OffsetDateTime
Instant
ist die grundlegende Bausteinklasse von java.time . Verwenden Sie für mehr Flexibilität OffsetDateTime
set to ZoneOffset.UTC
für denselben Zweck: einen Moment in UTC darstellen.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
Sie können dieses Objekt mithilfe PreparedStatement::setObject
von JDBC 4.2 oder höher an eine Datenbank senden .
myPreparedStatement.setObject( … , odt ) ;
Abrufen.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
java.sql.Date
Die java.sql.Date
Klasse ist auch schrecklich und veraltet.
Diese Klasse soll nur ein Datum ohne Tageszeit und ohne Zeitzone darstellen. Leider erbt diese Klasse in einem schrecklichen Hack eines Entwurfs, von java.util.Date
dem ein Moment dargestellt wird (ein Datum mit Uhrzeit in UTC). Diese Klasse gibt also lediglich vor, nur ein Datum zu sein, während sie tatsächlich eine Tageszeit und einen impliziten Versatz von UTC enthält. Das verursacht so viel Verwirrung. Verwenden Sie niemals diese Klasse.
java.time.LocalDate
Verwenden Sie stattdessen, um java.time.LocalDate
nur ein Datum (Jahr, Monat, Tag des Monats) ohne Tageszeit oder Zeitzone oder Versatz zu verfolgen.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate ld = LocalDate.now( z ) ; // Capture the current date as seen in the wall-clock time used by the people of a particular region (a time zone).
An die Datenbank senden.
myPreparedStatement.setObject( … , ld ) ;
Abrufen.
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
Das java.time- Framework ist in Java 8 und höher integriert. Diese Klassen verdrängen die lästigen alten Legacy - Datum-Zeit - Klassen wie java.util.Date
, Calendar
, & SimpleDateFormat
.
Weitere Informationen finden Sie im Oracle-Lernprogramm . Suchen Sie im Stapelüberlauf nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .
Das Joda-Time- Projekt, das sich jetzt im Wartungsmodus befindet , empfiehlt die Migration zu den Klassen java.time .
Sie können java.time- Objekte direkt mit Ihrer Datenbank austauschen . Verwenden Sie einen JDBC-Treiber, der mit JDBC 4.2 oder höher kompatibel ist . Keine Notwendigkeit für Zeichenfolgen, keine Notwendigkeit für java.sql.*
Klassen.
Woher bekomme ich die java.time-Klassen?
Die Klasse java.util.Date in Java repräsentiert einen bestimmten Zeitpunkt (z. B. 2013, 25. November, 16:30:45 Uhr bis auf Millisekunden), aber der Datentyp DATE in der Datenbank repräsentiert nur ein Datum (z. 2013, 25. November). Um zu verhindern, dass Sie versehentlich ein java.util.Date-Objekt für die Datenbank bereitstellen, können Sie in Java keinen SQL-Parameter direkt auf java.util.Date setzen:
PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work
Sie können dies jedoch weiterhin mit Gewalt / Absicht tun (dann werden Stunden und Minuten vom DB-Treiber ignoriert). Dies geschieht mit der Klasse java.sql.Date:
PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work
Ein java.sql.Date-Objekt kann einen Moment in der Zeit speichern (so dass es einfach ist, aus einem java.util.Date zu konstruieren), löst jedoch eine Ausnahme aus, wenn Sie versuchen, es nach den Stunden zu fragen (um sein Konzept, ein zu sein, durchzusetzen nur Datum). Es wird erwartet, dass der DB-Treiber diese Klasse erkennt und nur 0 für die Stunden verwendet. Versuche dies:
public static void main(String[] args) {
java.util.Date d1 = new java.util.Date(12345);//ms since 1970 Jan 1 midnight
java.sql.Date d2 = new java.sql.Date(12345);
System.out.println(d1.getHours());
System.out.println(d2.getHours());
}
java.util.Date
repräsentiert einen bestimmten Zeitpunkt mit Millisekundengenauigkeit. Es repräsentiert sowohl Datums- als auch Zeitinformationen ohne Zeitzone. Die Klasse java.util.Date implementiert die Schnittstelle Serializable, Cloneable und Comparable. Es wird vererbt java.sql.Date
, java.sql.Time
und java.sql.Timestamp
Schnittstellen.
java.sql.Date
erweitert die Klasse java.util.Date, die ein Datum ohne Zeitangabe darstellt und nur beim Umgang mit Datenbanken verwendet werden sollte. Um der Definition von SQL DATE zu entsprechen, müssen die von einer java.sql.Date
Instanz umschlossenen Millisekundenwerte "normalisiert" werden, indem die Stunden, Minuten, Sekunden und Millisekunden in der bestimmten Zeitzone, der die Instanz zugeordnet ist, auf Null gesetzt werden.
Es erbt alle öffentlichen Methoden java.util.Date
wie getHours()
, getMinutes()
, getSeconds()
, setHours()
, setMinutes()
, setSeconds()
. Da java.sql.Date
die Zeitinformationen nicht gespeichert werden, werden alle Zeitoperationen von überschrieben java.util.Date
und alle diese Methoden werden ausgelöst, java.lang.IllegalArgumentException
wenn sie aufgerufen werden, wie aus den Implementierungsdetails hervorgeht.