java.util.Date vs java.sql.Date


Risposte:


585

Congratulazioni, hai colpito la mia pipì preferita con JDBC: gestione della classe di date.

Fondamentalmente i database di solito supportano almeno tre forme di campi datetime che sono data, ora e data e ora. Ognuno di questi ha una classe corrispondente in JDBC e ognuno di essi si estendejava.util.Date . La semantica rapida di ciascuno di questi tre è il seguente:

  • java.sql.Datecorrisponde a SQL DATE, il che significa che memorizza anni, mesi e giorni, mentre ora, minuto, secondo e millisecondo vengono ignorati. Inoltre, sql.Datenon è legato ai fusi orari.
  • java.sql.Timecorrisponde a SQL TIME e, come dovrebbe essere ovvio, contiene solo informazioni su ora, minuti, secondi e millisecondi .
  • java.sql.Timestampcorrisponde a SQL TIMESTAMP che è la data esatta del nanosecondo ( nota che util.Datesupporta solo millisecondi! ) con precisione personalizzabile.

Uno dei bug più comuni quando si utilizzano i driver JDBC in relazione a questi tre tipi è che i tipi vengono gestiti in modo errato. Ciò significa che sql.Dateè specifico del fuso orario, sql.Timecontiene l'anno, il mese e il giorno attuali eccetera.

Finalmente: quale usare?

Dipende dal tipo SQL del campo, davvero. PreparedStatementha setter per tutti e tre i valori, #setDate()essendo quello per sql.Date, #setTime()per sql.Timee #setTimestamp()per sql.Timestamp.

Si noti che se si utilizza ps.setObject(fieldIndex, utilDateObject);si può effettivamente dare un normale util.Datealla maggior parte dei driver JDBC che li divorerà felicemente come se fosse del tipo corretto ma quando si richiedono i dati in seguito, è possibile notare che in realtà mancano cose.

Sto davvero dicendo che nessuna delle date dovrebbe essere usata affatto.

Quello che sto dicendo che salva i millisecondi / nanosecondi come semplici long e li converte in qualsiasi oggetto tu stia usando ( plug joda-time obbligatorio ). Un modo bizzarro che può essere fatto è quello di memorizzare il componente data come componente long e time come un altro, ad esempio in questo momento sarebbe 20100221 e 154536123. Questi numeri magici possono essere utilizzati nelle query SQL e saranno trasferibili dal database all'altro e ti permetterà di evitare completamente questa parte dell'API Date JDBC / Java: s.


17
Bella risposta. Ma la memorizzazione delle date non è un po 'ostile per il DBA?
Cherouvim,

26
Forse, tuttavia, i DBA generalmente tendono al loro RDBMS preferito e rifiutano tutto ciò che non riguarda quel RDBMS direttamente ( ti sto guardando, fan di Oracle ) mentre ci si aspetta che le applicazioni Java funzionino con tutti loro. Personalmente non mi piace affatto mettere la mia logica in DB.
Esko,

2
La mia colonna mysql è un datetime, ma sto facendo ps.setDate (new java.sql.Date (myObject.getCreatedDate (). GetTime ())); Sto perdendo la parte dei millisecondi, come risolvere questo?
Blankman,

2
Per non perdere i millisecondi: nuovo java.sql.Timestamp (utilDate.getTime ())
Kieveli

3
Ho detto che è un bug comune che è TZ specifico mentre per specifica non dovrebbe essere.
Esko,

60

LATE EDIT: a partire da Java 8 non dovresti usare né java.util.Datejava.sql.Datese non puoi assolutamente evitarlo, e preferisci invece usare il java.timepacchetto (basato su Joda) piuttosto che qualsiasi altra cosa. Se non sei su Java 8, ecco la risposta originale:


java.sql.Date- quando chiami metodi / costruttori di librerie che lo usano (come JDBC). Non altrimenti. Non si desidera introdurre dipendenze nelle librerie del database per applicazioni / moduli che non gestiscono esplicitamente JDBC.

java.util.Date- quando si usano librerie che lo usano. Altrimenti, il meno possibile, per diversi motivi:

  • È mutabile, il che significa che devi crearne una copia difensiva ogni volta che lo passi o lo restituisci da un metodo.

  • Non gestisce le date molto bene, cosa che le persone arretrate come la tua credono davvero che le lezioni di gestione delle date dovrebbero.

  • Ora, poiché juD non fa molto bene il suo lavoro, Calendarsono state introdotte le classi orribili . Sono anche mutabili e terribili con cui lavorare e dovrebbero essere evitati se non si ha altra scelta.

  • Esistono alternative migliori, come l' API Joda Time ( che potrebbe persino trasformarsi in Java 7 e diventare la nuova API ufficiale di gestione delle date - una ricerca veloce dice che non lo farà).

Se ritieni che sia eccessivo introdurre una nuova dipendenza come Joda, longnon sono poi così male da usare per i campi timestamp negli oggetti, anche se io stesso li avvolgo di solito quando li passiamo in giro, per sicurezza del tipo e come documentazione.


5
Perché dovremmo preferire java.time rispetto a java.sql durante la memorizzazione nel database? Sono davvero interessante nella dichiarazione, ma voglio capire perché :)
Jean-François Savard,

@ Jean-FrançoisSavard Spero che tu abbia trovato una risposta alla tua domanda da quando hai pubblicato quel commento - ma ecco una risposta, solo per completezza: è ancora perfettamente OK java.sql.Date con PreparedStatementecc! Ma quando lo passi in giro, usa un LocalDateoggetto che converti usando java.sql.Date.valueOfe java.sql.Date.valueOfquando lo imposti e riconvertilo il prima possibile usando java.sql.Date.toLocalDate - di nuovo, perché vuoi coinvolgere java.sql il meno possibile e perché è mutabile.
gustafc

19

L'unica volta da usare java.sql.Dateè in a PreparedStatement.setDate. Altrimenti, usa java.util.Date. Sta dicendo che ResultSet.getDaterestituisce a java.sql.Datema può essere assegnato direttamente a java.util.Date.


3
Ehm, ResultSet # getDate () restituisce sql.Date (che estende util.Date).
Esko,

3
@Esko - "Ehm", l'ho risolto prima che tu commentassi (e declassassi).
Paul Tomblin,

1
È importante notare che il motivo per cui un java.sql.Date può essere assegnato a un java.util.Date è perché il primo è una sottoclasse del secondo.
dj18

2
Perché 'dice' che a java.sql.Datepuò essere assegnato a a java.util.Datequando il primo estende il secondo? Qual è il punto che stai cercando di chiarire?
Marchese di Lorne,

11

Ho avuto lo stesso problema, il modo più semplice che ho trovato per inserire la data corrente in una dichiarazione preparata è questo:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));

2
non posso ottenere il fuso orario con questo.
erhanasikoglu,

4

tl; dr

Non usare nessuno dei due.

Nessuno dei due

java.util.Date vs java.sql.Date: quando usare quale e perché?

Entrambe queste classi sono terribili, imperfette nel design e nella realizzazione. Evita come il Coronavirus della peste .

Utilizzare invece le classi java.time , definite in JSR 310. Queste classi sono un framework leader del settore per la gestione della data e dell'ora. Questi soppiantare completamente le sanguinose classi legacy terribile come Date, Calendar, SimpleDateFormat, e così via.

java.util.Date

Il primo, java.util.Dateè pensato per rappresentare un momento in UTC, che significa un offset rispetto a UTC di zero ore-minuti-secondi.

java.time.Instant

Ora sostituito da java.time.Instant.

Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.

java.time.OffsetDateTime

Instantè la classe base di java.time . Per una maggiore flessibilità, utilizzare OffsetDateTimeset to ZoneOffset.UTCper lo stesso scopo: rappresentare un momento in UTC.

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;

È possibile inviare questo oggetto a un database utilizzando PreparedStatement::setObjectcon JDBC 4.2 o successivo.

myPreparedStatement.setObject(  , odt ) ;

Recuperare.

OffsetDateTime odt = myResultSet.getObject(  , OffsetDateTime.class ) ;

java.sql.Date

La java.sql.Dateclasse è anche terribile e obsoleta.

Questa classe intende rappresentare solo una data, senza ora del giorno e senza fuso orario. Sfortunatamente, in un terribile hack di un design, questa classe eredita da java.util.Datecui rappresenta un momento (una data con l'ora del giorno in UTC). Quindi questa classe sta semplicemente fingendo di essere solo data, mentre in realtà trasporta un fuso orario e un offset implicito di UTC. Questo provoca molta confusione. Non usare mai questa classe.

java.time.LocalDate

Invece, usa java.time.LocalDateper tracciare solo una data (anno, mese, giorno del mese) senza alcun orario del giorno né alcun fuso orario o offset.

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

Invia al database.

myPreparedStatement.setObject(  , ld ) ;

Recuperare.

LocalDate ld = myResultSet.getObject(  , LocalDate.class ) ;

Tabella dei tipi di data e ora in Java (sia legacy che moderna) e in SQL standard


Informazioni su java.time

Il framework java.time è integrato in Java 8 e versioni successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date, Calendar, e SimpleDateFormat.

Per saperne di più, consulta il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .

Il progetto Joda-Time , ora in modalità manutenzione , consiglia la migrazione alle classi java.time .

Puoi scambiare oggetti java.time direttamente con il tuo database. Utilizzare un driver JDBC conforme a JDBC 4.2 o successivo. Non c'è bisogno di stringhe, non c'è bisogno di java.sql.*classi.

Dove ottenere le classi java.time?

Tabella della libreria java.time da utilizzare con la versione di Java o Android


1
Upgrade per la sostituzione del riferimento di peste dal virus Corona. Più riconoscibile ora.
ankuranurag2

2

La classe java.util.Date in Java rappresenta un momento particolare nel tempo (ad esempio, 25 nov 2013 16:30:45 fino a millisecondi), ma il tipo di dati DATE nel DB rappresenta solo una data (ad es. 25 novembre 2013). Per evitare di fornire per errore un oggetto java.util.Date al DB, Java non consente di impostare direttamente un parametro SQL su java.util.Date:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work

Ma ti consente ancora di farlo con la forza / intenzione (quindi ore e minuti saranno ignorati dal driver DB). Questo viene fatto con la classe java.sql.Date:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work

Un oggetto java.sql.Date può memorizzare un momento nel tempo (in modo che sia facile da costruire da un java.util.Date) ma genererà un'eccezione se provi a chiederlo per le ore (per imporre il suo concetto di essere un solo data). Il driver DB dovrebbe riconoscere questa classe e usare solo 0 per le ore. Prova questo:

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

0

java.util.Daterappresenta un istante specifico nel tempo con precisione in millisecondi. Rappresenta sia la data sia l'ora senza fuso orario. La classe java.util.Date implementa l'interfaccia Serializable, Cloneable e Comparable. Esso viene ereditata da java.sql.Date, java.sql.Timee java.sql.Timestample interfacce.

java.sql.Dateestende la classe java.util.Date che rappresenta la data senza informazioni sull'ora e dovrebbe essere utilizzata solo quando si tratta di database. Per conformarsi alla definizione di SQL DATE, i valori in millisecondi racchiusi in java.sql.Dateun'istanza devono essere "normalizzati" impostando le ore, i minuti, i secondi e i millisecondi su zero nel particolare fuso orario a cui è associata l'istanza.

Si eredita tutti i metodi pubblici di java.util.Datecome getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). Poiché java.sql.Datenon memorizza le informazioni temporali, ha la priorità su tutte le operazioni temporali java.util.Datee tutti questi metodi vengono generati java.lang.IllegalArgumentExceptionse richiamati come evidente dai dettagli di implementazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.