java.util.Date vs java.sql.Date


Réponses:


585

Félicitations, vous avez frappé ma bête noire préférée avec JDBC: Gestion des classes de date.

Fondamentalement, les bases de données prennent généralement en charge au moins trois formes de champs datetime: date, heure et horodatage. Chacun d'eux a une classe correspondante dans JDBC et chacun d'eux s'étendjava.util.Date . La sémantique rapide de chacun de ces trois éléments est la suivante:

  • java.sql.Datecorrespond à SQL DATE, ce qui signifie qu'il stocke les années, les mois et les jours tandis que l' heure, la minute, la seconde et la milliseconde sont ignorées. De plus, sql.Daten'est pas lié aux fuseaux horaires.
  • java.sql.Timecorrespond à SQL TIME et comme cela devrait être évident, ne contient que des informations sur l' heure, les minutes, les secondes et les millisecondes .
  • java.sql.Timestampcorrespond à SQL TIMESTAMP qui est la date exacte à la nanoseconde ( notez que cela util.Datene prend en charge que les millisecondes! ) avec une précision personnalisable.

L'un des bogues les plus courants lors de l'utilisation de pilotes JDBC par rapport à ces trois types est que les types sont traités de manière incorrecte. Cela signifie que le sql.Datefuseau horaire est spécifique, sql.Timecontient l'année, le mois et le jour en cours et cetera et cetera.

Enfin: lequel utiliser?

Cela dépend vraiment du type SQL du champ. PreparedStatementa des setters pour les trois valeurs, #setDate()étant celle pour sql.Date, #setTime()pour sql.Timeet #setTimestamp()pour sql.Timestamp.

Notez que si vous utilisez, ps.setObject(fieldIndex, utilDateObject);vous pouvez réellement donner un normal util.Dateà la plupart des pilotes JDBC qui le dévoreront volontiers comme s'il était du type correct, mais lorsque vous demanderez les données par la suite, vous remarquerez peut-être qu'il vous manque réellement des éléments.

Je dis vraiment qu'aucune des dates ne devrait être utilisée du tout.

Ce que je dis, c'est que vous enregistrez les millisecondes / nanosecondes en tant que longs et les convertissez en tous les objets que vous utilisez (prise Joda-time obligatoire ). Une manière hacky qui peut être faite est de stocker le composant date comme un composant long et le temps comme un autre, par exemple actuellement 20100221 et 154536123. Ces nombres magiques peuvent être utilisés dans les requêtes SQL et seront portables de la base de données à l'autre et vous permettra d'éviter complètement cette partie de l'API JDBC / Java Date.


17
Bonne réponse. Mais le stockage des dates n'est-il pas un peu hostile pour le DBA?
cherouvim

26
Peut-être, cependant, les DBA: ont généralement tendance à choisir leur SGBDR de choix et à rejeter tout ce qui ne concerne pas directement ce SGBDR ( je vous regarde, fans d'Oracle ) tandis que les applications Java sont censées fonctionner avec tous. Personnellement, je n'aime pas du tout mettre ma logique dans DB.
Esko

2
Ma colonne mysql est un datetime, mais faisant ps.setDate (new java.sql.Date (myObject.getCreatedDate (). GetTime ())); Je perds la partie en millisecondes, comment y remédier?
Blankman

2
Pour ne pas perdre vos millisecondes: nouveau java.sql.Timestamp (utilDate.getTime ())
Kieveli

3
J'ai mentionné que c'est un bug commun qu'il est spécifique à TZ alors qu'il ne devrait pas l'être par spécification.
Esko

60

MODIFICATION TARDIVE: à partir de Java 8, vous ne devez utiliser ni java.util.Dateni java.sql.Datesi vous pouvez l'éviter, et préférez plutôt utiliser le java.timepackage (basé sur Joda) plutôt que toute autre chose. Si vous n'êtes pas sur Java 8, voici la réponse d'origine:


java.sql.Date- lorsque vous appelez des méthodes / constructeurs de bibliothèques qui l'utilisent (comme JDBC). Pas autrement. Vous ne voulez pas introduire de dépendances dans les bibliothèques de base de données pour les applications / modules qui ne traitent pas explicitement avec JDBC.

java.util.Date- lors de l'utilisation des bibliothèques qui l'utilisent. Sinon, le moins possible, pour plusieurs raisons:

  • Il est modifiable, ce qui signifie que vous devez en faire une copie défensive chaque fois que vous le transmettez ou le renvoyez d'une méthode.

  • Il ne gère pas très bien les dates, ce qui est vrai pour les gens comme les vôtres.

  • Maintenant, parce que juD ne fait pas très bien son travail, les Calendarclasses horribles ont été introduites. Ils sont également mutables et horribles à travailler et doivent être évités si vous n'avez pas le choix.

  • Il existe de meilleures alternatives, comme l' API Joda Time ( qui pourrait même devenir Java 7 et devenir la nouvelle API officielle de gestion des dates - une recherche rapide dit que non).

Si vous pensez qu'il est exagéré d'introduire une nouvelle dépendance comme Joda, ce longn'est pas si mal à utiliser pour les champs d'horodatage dans les objets, bien que moi-même je les enveloppe généralement dans juD lors de leur passage, pour la sécurité des types et comme documentation.


5
Pourquoi devrions-nous préférer java.time à java.sql lors du stockage dans la base de données? Je suis vraiment intéressant dans la déclaration, mais je veux comprendre pourquoi :)
Jean-François Savard

@ Jean-FrançoisSavard J'espère que vous avez trouvé une réponse à votre question depuis que vous avez posté ce commentaire - mais voici une réponse, juste pour être complet: c'est toujours parfaitement OK java.sql.Date avec PreparedStatementetc! Mais lorsque vous le faites circuler, utilisez un fichier que LocalDatevous convertissez en utilisant java.sql.Date.valueOfet java.sql.Date.valueOfen le définissant, et reconvertissez-le le plus tôt possible en utilisant java.sql.Date.toLocalDate- encore une fois, parce que vous voulez impliquer java.sql le moins possible et parce qu'il est mutable.
Gustafc

19

Le seul moment d'utilisation java.sql.Dateest dans un PreparedStatement.setDate. Sinon, utilisez java.util.Date. C'est révélateur qui ResultSet.getDaterenvoie un java.sql.Datemais il peut être affecté directement à un java.util.Date.


3
Ehm, ResultSet # getDate () renvoie sql.Date (qui étend util.Date).
Esko

3
@Esko - "Ehm", j'ai corrigé cela avant de commenter (et de voter).
Paul Tomblin

1
Il est important de noter que la raison pour laquelle un fichier java.sql.Date peut être affecté à un fichier java.util.Date est que le premier est une sous-classe du second.
dj18

2
Pourquoi est-il «révélateur» qu'un a java.sql.Datepuisse être attribué à un java.util.Datelorsque le premier étend le second? Quel est le point que vous essayez de faire valoir?
Marquis de Lorne

11

J'ai eu le même problème, la manière la plus simple que j'ai trouvée pour insérer la date actuelle dans une déclaration préparée est celle-ci:

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

2
ne peut pas obtenir le fuseau horaire avec cela.
erhanasikoglu

4

tl; dr

N'utilisez ni l'un ni l'autre.

Ni

java.util.Date vs java.sql.Date: quand utiliser quoi et pourquoi?

Ces deux classes sont terribles, défectueuses dans leur conception et leur mise en œuvre. Évitez comme le coronavirus de la peste .

Utilisez plutôt les classes java.time , définies dans JSR 310. Ces classes sont un cadre de pointe pour travailler avec la gestion de la date et de l'heure. Ces classes remplacer entièrement les anciens affreux sanglants tels que Date, Calendar, SimpleDateFormat, et autres.

java.util.Date

Le premier java.util.Dateest censé représenter un moment en UTC, ce qui signifie un décalage par rapport à UTC de zéro heure-minute-seconde.

java.time.Instant

Maintenant remplacé par java.time.Instant.

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

java.time.OffsetDateTime

Instantest la classe de base de java.time . Pour plus de flexibilité, utilisez OffsetDateTimeset to ZoneOffset.UTCdans le même but: représenter un moment en UTC.

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

Vous pouvez envoyer cet objet à une base de données à l'aide PreparedStatement::setObjectde JDBC 4.2 ou version ultérieure.

myPreparedStatement.setObject(  , odt ) ;

Récupérer.

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

java.sql.Date

La java.sql.Dateclasse est également terrible et obsolète.

Cette classe est censée représenter une date uniquement, sans heure et sans fuseau horaire. Malheureusement, dans un terrible piratage d'un design, cette classe hérite de java.util.Datelaquelle représente un moment (une date avec l'heure en UTC). Ainsi, cette classe prétend simplement être à date uniquement, tout en portant en fait une heure et un décalage implicite de l'UTC. Cela cause tellement de confusion. N'utilisez jamais cette classe.

java.time.LocalDate

Au lieu de cela, utilisez java.time.LocalDatepour suivre uniquement une date (année, mois, jour du mois) sans heure, ni fuseau horaire ni décalage.

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

Envoyer à la base de données.

myPreparedStatement.setObject(  , ld ) ;

Récupérer.

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

Tableau des types date-heure en Java (ancien et moderne) et en SQL standard


À propos de java.time

Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciens gênants hérités des classes date-heure tels que java.util.Date, Calendar, et SimpleDateFormat.

Pour en savoir plus, consultez le didacticiel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .

Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers les classes java.time .

Vous pouvez échanger des objets java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou version ultérieure. Pas besoin de cordes, pas besoin dejava.sql.* cours.

Où obtenir les classes java.time?

Tableau de la bibliothèque java.time à utiliser avec quelle version de Java ou Android


1
Vote positif pour le remplacement de la référence Plague par le virus Corona. Plus relatable maintenant.
ankuranurag2

2

La classe java.util.Date en Java représente un moment particulier dans le temps (par exemple, le 25 novembre 2013 à 16 h 30 min 45 s), mais le type de données DATE dans la base de données représente uniquement une date (par exemple, 25 novembre 2013). Pour vous empêcher de fournir un objet java.util.Date à la base de données par erreur, Java ne vous permet pas de définir directement un paramètre SQL sur java.util.Date:

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

Mais cela vous permet toujours de le faire par force / intention (alors les heures et les minutes seront ignorées par le pilote DB). Cela se fait avec la classe java.sql.Date:

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

Un objet java.sql.Date peut stocker un instant dans le temps (de sorte qu'il est facile de construire à partir d'un java.util.Date) mais lèvera une exception si vous essayez de lui demander des heures (pour appliquer son concept d'être un date uniquement). Le pilote de base de données devrait reconnaître cette classe et utiliser simplement 0 pour les heures. Essaye ça:

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.Datereprésente un instant précis dans le temps avec une précision en millisecondes. Il représente à la fois des informations de date et d'heure sans fuseau horaire. La classe java.util.Date implémente une interface sérialisable, clonable et comparable. Il est hérité parjava.sql.Date , java.sql.Timeet java.sql.Timestampinterfaces.

java.sql.Dateétend la classe java.util.Date qui représente les informations de date sans heure et ne doit être utilisée que pour les bases de données. Pour se conformer à la définition de SQL DATE, les valeurs en millisecondes encapsulées par une java.sql.Dateinstance doivent être `` normalisées '' en définissant les heures, minutes, secondes et millisecondes sur zéro dans le fuseau horaire particulier auquel l'instance est associée.

Il hérite de toutes les méthodes publiques de java.util.Datetelles que getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). Comme java.sql.Dateil ne stocke pas les informations de temps, il remplace toutes les opérations de temps java.util.Dateet toutes ces méthodes sont lancées java.lang.IllegalArgumentExceptionsi elles sont invoquées comme le montrent leurs détails d'implémentation.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.