java.util.Date vs java.sql.Date

Respuestas:

585

Enhorabuena, has golpeado a mi mascota favorita con JDBC: manejo de la clase de fecha.

Básicamente, las bases de datos generalmente admiten al menos tres formas de campos de fecha y hora que son fecha, hora y marca de tiempo. Cada uno de estos tiene una clase correspondiente en JDBC y cada uno de ellos se extiendejava.util.Date . La semántica rápida de cada uno de estos tres es la siguiente:

  • java.sql.Datecorresponde a SQL DATE, lo que significa que almacena años, meses y días, mientras que las horas, minutos, segundos y milisegundos se ignoran. Además sql.Dateno está vinculado a zonas horarias.
  • java.sql.Timecorresponde a SQL TIME y, como debería ser obvio, solo contiene información sobre horas, minutos, segundos y milisegundos .
  • java.sql.Timestampcorresponde a SQL TIMESTAMP, que es la fecha exacta al nanosegundo ( tenga en cuenta que util.Datesolo admite milisegundos ) con precisión personalizable.

Uno de los errores más comunes cuando se utilizan controladores JDBC en relación con estos tres tipos es que los tipos se manejan incorrectamente. Esto significa que sql.Datees específico de la zona horaria, sql.Timecontiene el año, mes y día actuales, etc.

Finalmente: ¿Cuál usar?

Depende del tipo de SQL del campo, realmente. PreparedStatementtiene establecedores para los tres valores, #setDate()siendo uno para sql.Date, #setTime()para sql.Timey #setTimestamp()para sql.Timestamp.

Tenga en cuenta que si lo usa ps.setObject(fieldIndex, utilDateObject);, puede dar una normalidad util.Datea la mayoría de los controladores JDBC que felizmente lo devorarán como si fuera del tipo correcto, pero cuando solicite los datos después, puede notar que realmente le faltan cosas.

Realmente estoy diciendo que ninguna de las Fechas debería usarse en absoluto.

Lo que estoy diciendo es que guarde los milisegundos / nanosegundos como largos simples y conviértalos a los objetos que esté utilizando ( conector obligatorio de tiempo de joda ). Una forma hacky que se puede hacer es almacenar el componente de fecha como un componente de tiempo largo como otro, por ejemplo, ahora sería 20100221 y 154536123. Estos números mágicos se pueden usar en consultas SQL y serán portables de la base de datos a otra y le permitirá evitar esta parte de JDBC / Java Date API: s por completo.

Esko
fuente
17
Buena respuesta. Pero, ¿no es el almacenamiento de fechas siempre que sea un poco hostil para el DBA?
cherouvim
26
Quizás, sin embargo, los DBA generalmente tienden a elegir su RDBMS y rechazan todo lo que no se trata de ese RDBMS directamente (los estoy mirando, fanáticos de Oracle ), mientras que se espera que las aplicaciones Java funcionen con todos ellos. Personalmente, no me gusta poner mi lógica en DB en absoluto.
Esko
2
Mi columna mysql es una fecha y hora, pero está haciendo ps.setDate (new java.sql.Date (myObject.getCreatedDate (). GetTime ())); Estoy perdiendo la porción de milisegundos, ¿cómo solucionar esto?
Blankman
2
Para no perder tus milisegundos: nuevo java.sql.Timestamp (utilDate.getTime ())
Kieveli
3
Mencioné que es un error común que es específico de TZ mientras que, por especificación, no debería serlo.
Esko
60

EDITAR TARDE: A partir de 8 de Java que puedes usar ni java.util.Datetampoco java.sql.Datesi es posible en absoluto evitarlo, y en su lugar prefieren utilizar el java.timepaquete (basado en Joda) más que cualquier otra cosa. Si no está en Java 8, aquí está la respuesta original:


java.sql.Date- cuando llama a métodos / constructores de bibliotecas que lo usan (como JDBC). De otro modo no. No desea introducir dependencias en las bibliotecas de bases de datos para aplicaciones / módulos que no tratan explícitamente con JDBC.

java.util.Date- cuando se usan bibliotecas que lo usan. De lo contrario, lo menos posible, por varias razones:

  • Es mutable, lo que significa que debe hacer una copia defensiva cada vez que se lo pasa o lo devuelve desde un método.

  • No maneja las fechas muy bien, lo que realmente hace retroceder a personas como la suya, piensa que las clases de manejo de fechas deberían hacerlo.

  • Ahora, debido a que juD no hace su trabajo muy bien, Calendarse introdujeron las clases horribles . También son mutables y horribles para trabajar, y deben evitarse si no tiene otra opción.

  • Hay mejores alternativas, como la API de Joda Time ( que incluso podría convertirse en Java 7 y convertirse en la nueva API oficial de manejo de fechas , una búsqueda rápida dice que no lo hará).

Si siente que es excesivo introducir una nueva dependencia como Joda, longno es tan malo usarlo para campos de marca de tiempo en objetos, aunque yo mismo los envuelvo en juD cuando los paso, por seguridad de tipo y como documentación.

gustafc
fuente
55
¿Por qué deberíamos preferir java.time sobre java.sql al almacenar en la base de datos? Soy realmente interesante en la declaración, pero quiero entender por qué :)
Jean-François Savard
@ Jean-FrançoisSavard Espero que hayas encontrado una respuesta a tu pregunta desde que publicaste ese comentario, pero aquí hay una respuesta, solo por el bien de la totalidad: ¡todavía está perfectamente bien java.sql.Date con PreparedStatementetc.! Pero cuando lo pase, use uno LocalDateque convierta usando java.sql.Date.valueOfy java.sql.Date.valueOfcuando lo configure, y vuelva a convertirlo lo antes posible usando java.sql.Date.toLocalDate , nuevamente, porque desea involucrar a java.sql lo menos posible y porque es mutable.
gustafc
19

El único momento para usar java.sql.Datees en a PreparedStatement.setDate. De lo contrario, use java.util.Date. Es revelador que ResultSet.getDatedevuelve a java.sql.Datepero se puede asignar directamente a a java.util.Date.

Paul Tomblin
fuente
3
Ehm, ResultSet # getDate () devuelve sql.Date (que extiende util.Date).
Esko
3
@Esko - "Ehm", arreglé eso antes de comentar (y rechazar)
Paul Tomblin
1
Es importante tener en cuenta que la razón por la que se puede asignar un java.sql.Date a un java.util.Date es porque el primero es una subclase del segundo.
dj18
2
¿Por qué 'dice' que java.sql.Datese puede asignar a a java.util.Datecuando el primero extiende al segundo? ¿Cuál es el punto que estás tratando de hacer?
Marqués de Lorne
11

Tuve el mismo problema, la forma más fácil que encontré para insertar la fecha actual en una declaración preparada es esta:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Israelm
fuente
2
No puedo obtener zona horaria con esto.
erhanasikoglu
4

tl; dr

No use ninguno.

Ninguno

java.util.Date vs java.sql.Date: ¿cuándo usar cuál y por qué?

Ambas clases son terribles, defectuosas en diseño e implementación. Evite como el coronavirus de la peste .

En su lugar, use las clases java.time , definidas en JSR 310. Estas clases son un marco líder en la industria para trabajar con el manejo de fecha y hora. Estos suplantar totalmente las clases horrible sangriento legado como Date, Calendar, SimpleDateFormat, y tal.

java.util.Date

El primero, java.util.Dateestá destinado a representar un momento en UTC, lo que significa un desplazamiento de UTC de cero horas-minutos-segundos.

java.time.Instant

Ahora reemplazado por java.time.Instant.

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

java.time.OffsetDateTime

Instantes la clase básica de bloques de construcción de java.time . Para mayor flexibilidad, use OffsetDateTimeset to ZoneOffset.UTCpara el mismo propósito: representar un momento en UTC.

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

Puede enviar este objeto a una base de datos mediante el uso PreparedStatement::setObjectde JDBC 4.2 o posterior.

myPreparedStatement.setObject(  , odt ) ;

Recuperar.

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

java.sql.Date

La java.sql.Dateclase también es terrible y obsoleta.

Esta clase está destinada a representar solo una fecha, sin una hora del día y sin zona horaria. Desafortunadamente, en un terrible truco de un diseño, esta clase hereda de la java.util.Datecual representa un momento (una fecha con la hora del día en UTC). Por lo tanto, esta clase solo pretende ser solo de fecha, mientras que en realidad lleva una hora del día y una compensación implícita de UTC. Esto causa mucha confusión. Nunca uses esta clase.

java.time.LocalDate

En su lugar, use java.time.LocalDatepara rastrear solo una fecha (año, mes, día del mes) sin ninguna hora del día ni zona horaria o compensación.

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

Enviar a la base de datos.

myPreparedStatement.setObject(  , ld ) ;

Recuperar.

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

Tabla de tipos de fecha y hora en Java (tanto heredados como modernos) y en SQL estándar


Sobre java.time

El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date, Calendar, y SimpleDateFormat.

Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

Puede intercambiar objetos java.time directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. No hay necesidad de cadenas, no hay necesidad de java.sql.*clases.

¿Dónde obtener las clases java.time?

Tabla de qué biblioteca java.time usar con qué versión de Java o Android

Albahaca Bourque
fuente
1
Voto a favor de reemplazar la referencia de la peste por el virus Corona. Más relatable ahora.
ankuranurag2
2

La clase java.util.Date en Java representa un momento particular en el tiempo (por ejemplo, 25 de noviembre de 2013, 16:30:45 a milisegundos), pero el tipo de datos DATE en la base de datos representa solo una fecha (por ejemplo, 2013 25 de noviembre). Para evitar que proporcione un objeto java.util.Date a la base de datos por error, Java no le permite establecer un parámetro SQL en java.util.Date directamente:

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

Pero aún así le permite hacerlo por la fuerza / intención (entonces el controlador de DB ignorará las horas y los minutos). Esto se hace con la clase java.sql.Date:

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

Un objeto java.sql.Date puede almacenar un momento en el tiempo (por lo que es fácil de construir a partir de un java.util.Date) pero arrojará una excepción si intenta pedirle las horas (para hacer cumplir su concepto de ser un fecha solamente). Se espera que el controlador de base de datos reconozca esta clase y solo use 0 durante las horas. Prueba esto:

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());
}
Kent Tong
fuente
0

java.util.Daterepresenta un instante específico en el tiempo con precisión de milisegundos. Representa información de fecha y hora sin zona horaria. La clase java.util.Date implementa una interfaz serializable, clonable y comparable. Se heredado por java.sql.Date, java.sql.Timey java.sql.Timestampinterfaces.

java.sql.Dateextiende la clase java.util.Date que representa la información de fecha sin hora y debe usarse solo cuando se trata de bases de datos. Para cumplir con la definición de SQL DATE, los valores de milisegundos envueltos por una java.sql.Dateinstancia deben 'normalizarse' configurando las horas, minutos, segundos y milisegundos a cero en la zona horaria particular con la que está asociada la instancia.

Se hereda todos los métodos públicos de java.util.Datetales como getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). Como java.sql.Dateno almacena la información de tiempo, anula todas las operaciones de tiempo java.util.Datey todos estos métodos arrojan java.lang.IllegalArgumentExceptionsi se invocan como es evidente a partir de sus detalles de implementación.

Abdul Alim Shakir
fuente