Respostas:
Parabéns, você acertou minha irritação favorita com JDBC: Data class handling.
Basicamente, os bancos de dados geralmente suportam pelo menos três formas de campos de data e hora que são data, hora e carimbo de data e hora. Cada um deles tem uma classe correspondente no JDBC e cada um deles se estendejava.util.Date
. A semântica rápida de cada um desses três é a seguinte:
java.sql.Date
corresponde ao SQL DATE, o que significa que armazena anos, meses e dias, enquanto hora, minuto, segundo e milissegundo são ignorados. Além disso, sql.Date
não está vinculado a fusos horários.java.sql.Time
corresponde ao SQL TIME e, como deve ser óbvio, contém apenas informações sobre hora, minutos, segundos e milissegundos .java.sql.Timestamp
corresponde ao SQL TIMESTAMP, que é a data exata do nanossegundo ( observe que util.Date
apenas suporta milissegundos! ) com precisão personalizável.Um dos erros mais comuns ao usar drivers JDBC em relação a esses três tipos é que os tipos são manipulados incorretamente. Isso significa que sql.Date
é específico do fuso horário, sql.Time
contém o ano, o mês e o dia atuais etc.
Depende do tipo SQL do campo, realmente. PreparedStatement
possui setters para todos os três valores, #setDate()
sendo esse para sql.Date
, #setTime()
para sql.Time
e #setTimestamp()
para sql.Timestamp
.
Observe que, se você usar, ps.setObject(fieldIndex, utilDateObject);
poderá realmente fornecer um normal util.Date
à maioria dos drivers JDBC, que o devorarão como se fossem do tipo correto, mas, quando você solicitar os dados posteriormente, poderá perceber que realmente está faltando alguma coisa.
O que estou dizendo é que salva os milissegundos / nanossegundos em longos simples e os converte em qualquer objeto que você estiver usando ( plug obrigatório de tempo de joda ). Uma maneira hacky que pode ser feita é armazenar o componente de data como um componente de tempo e tempo como outro, por exemplo, agora seria 20100221 e 154536123. Esses números mágicos podem ser usados em consultas SQL e serão portáteis do banco de dados para outro e permitirá que você evite completamente essa parte da API JDBC / Java Date API: s.
EDIT LATE: Começando com Java 8 você deve usar nem java.util.Date
nem java.sql.Date
se você puder evitar isso, e em vez disso preferem usar o java.time
pacote (com base em Joda) em vez de qualquer outra coisa. Se você não está no Java 8, aqui está a resposta original:
java.sql.Date
- quando você chama métodos / construtores de bibliotecas que o utilizam (como JDBC). Não de outra forma. Você não deseja introduzir dependências nas bibliotecas de banco de dados para aplicativos / módulos que não lidam explicitamente com o JDBC.
java.util.Date
- ao usar bibliotecas que o utilizam. Caso contrário, o mínimo possível, por vários motivos:
É mutável, o que significa que você precisa fazer uma cópia defensiva toda vez que passa ou devolve um método.
Ele não lida com datas muito bem, o que, de trás para frente, pessoas como a sua, acham que as aulas de manipulação de datas deveriam.
Agora, como o juD não faz seu trabalho muito bem, as horríveis Calendar
classes foram introduzidas. Eles também são mutáveis e difíceis de trabalhar, e devem ser evitados se você não tiver escolha.
Existem alternativas melhores, como a API Joda Time ( que pode até entrar no Java 7 e se tornar a nova API oficial de manipulação de datas - uma pesquisa rápida diz que não).
Se você acha que é exagero introduzir uma nova dependência como Joda, long
não é tão ruim usar os campos de registro de data e hora nos objetos, embora eu mesmo os envolva em juD quando os repassamos, para segurança de tipo e como documentação.
java.sql.Date
com PreparedStatement
etc! Mas quando você está distribuindo, use um LocalDate
que você converta usando java.sql.Date.valueOf
e java.sql.Date.valueOf
ao configurá-lo, e converta-o o mais cedo possível usando java.sql.Date.toLocalDate
- novamente, porque você deseja envolver o java.sql o mínimo possível e porque é mutável.
O único momento para usar java.sql.Date
é em PreparedStatement.setDate
. Caso contrário, use java.util.Date
. Está dizendo que ResultSet.getDate
retorna a, java.sql.Date
mas pode ser atribuído diretamente a java.util.Date
.
java.sql.Date
pode ser atribuído a a java.util.Date
quando o primeiro estende o último? Qual é o ponto que você está tentando enfatizar?
Eu tive o mesmo problema, a maneira mais fácil de inserir a data atual em uma declaração preparada é esta:
preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Não use nenhum.
java.time.Instant
substitui java.util.Date
java.time.LocalDate
substitui java.sql.Date
java.util.Date vs java.sql.Date: quando usar qual e por quê?
Ambas as classes são terríveis, com falhas no design e na implementação. Evite como o coronavírus da peste .
Em vez disso, use as classes java.time , definidas no JSR 310. Essas classes são uma estrutura líder do setor para trabalhar com manipulação de data e hora. Estes suplantar inteiramente os sangrentos aulas terrível legado, como Date
, Calendar
, SimpleDateFormat
, e tal.
java.util.Date
O primeiro, java.util.Date
pretende representar um momento no UTC, significando um deslocamento do UTC de zero horas-minutos-segundos.
java.time.Instant
Agora substituído por java.time.Instant
.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
java.time.OffsetDateTime
Instant
é a classe básica de blocos de construção do java.time . Para obter mais flexibilidade, use OffsetDateTime
set ZoneOffset.UTC
para o mesmo objetivo: representar um momento no UTC.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
Você pode enviar esse objeto para um banco de dados usando PreparedStatement::setObject
com JDBC 4.2 ou posterior.
myPreparedStatement.setObject( … , odt ) ;
Recuperar.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
java.sql.Date
A java.sql.Date
turma também é terrível e obsoleta.
Esta classe deve representar apenas uma data, sem uma hora do dia e sem um fuso horário. Infelizmente, em um terrível hack de um design, essa classe é herdada da java.util.Date
qual representa um momento (uma data com a hora do dia no UTC). Portanto, essa classe está apenas fingindo ser apenas de data, enquanto na verdade carrega um deslocamento de hora do dia e implícito do UTC. Isso causa muita confusão. Nunca use esta classe.
java.time.LocalDate
Em vez disso, use java.time.LocalDate
para rastrear apenas uma data (ano, mês, dia do mês) sem nenhuma hora do dia nem fuso horário ou deslocamento.
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 para o banco de dados.
myPreparedStatement.setObject( … , ld ) ;
Recuperar.
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
A estrutura java.time é integrada ao Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date
, Calendar
, e SimpleDateFormat
.
Para saber mais, consulte o Tutorial do Oracle . E procure Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .
O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para as classes java.time .
Você pode trocar objetos java.time diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou posterior. Não há necessidade de strings, não há necessidade de java.sql.*
classes.
Onde obter as classes java.time?
A classe java.util.Date em Java representa um momento específico (por exemplo, 25 de novembro de 2013 às 16:30:45 até milissegundos), mas o tipo de dados DATE no banco de dados representa apenas uma data (por exemplo, 2013 25 de novembro). Para impedir que você forneça um objeto java.util.Date ao banco de dados por engano, Java não permite que você defina um parâmetro SQL como java.util.Date diretamente:
PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work
Mas ainda permite que você faça isso por força / intenção (horas e minutos serão ignorados pelo driver do DB). Isso é feito com a classe java.sql.Date:
PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work
Um objeto java.sql.Date pode armazenar um momento no tempo (para que seja fácil construir a partir de um java.util.Date), mas lançará uma exceção se você tentar perguntar por horas (para reforçar seu conceito de ser um somente data). O driver DB deve reconhecer essa classe e usar apenas 0 para as horas. Tente o seguinte:
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 um instante específico no tempo com precisão de milissegundos. Representa informações de data e hora sem fuso horário. A classe java.util.Date implementa a interface Serializable, Cloneable e Comparable. É herdado por java.sql.Date
, java.sql.Time
e java.sql.Timestamp
interfaces.
java.sql.Date
estende a classe java.util.Date, que representa informações de data sem hora, e deve ser usada apenas ao lidar com bancos de dados. Para estar em conformidade com a definição de SQL DATE, os valores de milissegundos agrupados por uma java.sql.Date
instância devem ser 'normalizados', definindo as horas, minutos, segundos e milissegundos para zero no fuso horário específico ao qual a instância está associada.
Ele herda todos os métodos públicos de java.util.Date
tais como getHours()
, getMinutes()
, getSeconds()
, setHours()
, setMinutes()
, setSeconds()
. Como java.sql.Date
não armazena as informações de horário, elas substituem todas as operações de horário java.util.Date
e todos esses métodos são lançados java.lang.IllegalArgumentException
se invocados como evidentes nos detalhes de implementação.