Respuestas:
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.Date
corresponde 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.Date
no está vinculado a zonas horarias.java.sql.Time
corresponde a SQL TIME y, como debería ser obvio, solo contiene información sobre horas, minutos, segundos y milisegundos .java.sql.Timestamp
corresponde a SQL TIMESTAMP, que es la fecha exacta al nanosegundo ( tenga en cuenta que util.Date
solo 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.Date
es específico de la zona horaria, sql.Time
contiene el año, mes y día actuales, etc.
Depende del tipo de SQL del campo, realmente. PreparedStatement
tiene establecedores para los tres valores, #setDate()
siendo uno para sql.Date
, #setTime()
para sql.Time
y #setTimestamp()
para sql.Timestamp
.
Tenga en cuenta que si lo usa ps.setObject(fieldIndex, utilDateObject);
, puede dar una normalidad util.Date
a 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.
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.
EDITAR TARDE: A partir de 8 de Java que puedes usar ni java.util.Date
tampoco java.sql.Date
si es posible en absoluto evitarlo, y en su lugar prefieren utilizar el java.time
paquete (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, Calendar
se 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, long
no 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.
java.sql.Date
con PreparedStatement
etc.! Pero cuando lo pase, use uno LocalDate
que convierta usando java.sql.Date.valueOf
y java.sql.Date.valueOf
cuando 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.
El único momento para usar java.sql.Date
es en a PreparedStatement.setDate
. De lo contrario, use java.util.Date
. Es revelador que ResultSet.getDate
devuelve a java.sql.Date
pero se puede asignar directamente a a java.util.Date
.
java.sql.Date
se puede asignar a a java.util.Date
cuando el primero extiende al segundo? ¿Cuál es el punto que estás tratando de hacer?
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()));
No use ninguno.
java.time.Instant
reemplaza java.util.Date
java.time.LocalDate
reemplaza java.sql.Date
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.Date
está 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
Instant
es la clase básica de bloques de construcción de java.time . Para mayor flexibilidad, use OffsetDateTime
set to ZoneOffset.UTC
para 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::setObject
de JDBC 4.2 o posterior.
myPreparedStatement.setObject( … , odt ) ;
Recuperar.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
java.sql.Date
La java.sql.Date
clase 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.Date
cual 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.LocalDate
para 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 ) ;
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?
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());
}
java.util.Date
representa 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.Time
y java.sql.Timestamp
interfaces.
java.sql.Date
extiende 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.Date
instancia 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.Date
tales como getHours()
, getMinutes()
, getSeconds()
, setHours()
, setMinutes()
, setSeconds()
. Como java.sql.Date
no almacena la información de tiempo, anula todas las operaciones de tiempo java.util.Date
y todos estos métodos arrojan java.lang.IllegalArgumentException
si se invocan como es evidente a partir de sus detalles de implementación.