java.util.Date vs java.sql.Date

501

java.util.Datevs java.sql.Date: quando usar qual e por quê?

flybywire
fonte

Respostas:

585

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.Datecorresponde 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.Datenão está vinculado a fusos horários.
  • java.sql.Timecorresponde ao SQL TIME e, como deve ser óbvio, contém apenas informações sobre hora, minutos, segundos e milissegundos .
  • java.sql.Timestampcorresponde ao SQL TIMESTAMP, que é a data exata do nanossegundo ( observe que util.Dateapenas 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.Timecontém o ano, o mês e o dia atuais etc.

Finalmente: Qual usar?

Depende do tipo SQL do campo, realmente. PreparedStatementpossui setters para todos os três valores, #setDate()sendo esse para sql.Date, #setTime()para sql.Timee #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.

Estou realmente dizendo que nenhuma das datas deve ser usada.

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.

Esko
fonte
17
Boa resposta. Mas o armazenamento de datas não é tão hostil para o DBA?
21410 Cherouvim
26
Talvez, no entanto, os DBAs geralmente tendam ao RDBMS de sua escolha e rejeitem tudo o que não interessa diretamente ao RDBMS ( estou olhando para você, fãs do Oracle ), enquanto se espera que os aplicativos Java funcionem com todos eles. Pessoalmente, não gosto de colocar minha lógica no DB.
Esko
2
Minha coluna mysql é uma data e hora, mas está fazendo o ps.setDate (novo java.sql.Date (myObject.getCreatedDate (). GetTime ())); Estou perdendo a parte dos milissegundos, como consertar isso?
21412 Blankman
2
Para não perder seus milissegundos: nova java.sql.Timestamp (utilDate.getTime ())
Kieveli
3
Eu mencionei que é um bug comum que é específico para TZ, embora por especificação não deva ser.
Esko
60

EDIT LATE: Começando com Java 8 você deve usar nem java.util.Datenem java.sql.Datese você puder evitar isso, e em vez disso preferem usar o java.timepacote (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 Calendarclasses 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, longnã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.

gustafc
fonte
5
Por que devemos preferir java.time sobre java.sql ao armazenar no banco de dados? Estou muito interessante na demonstração, mas eu quero entender por que :)
Jean-François Savard
@ Jean-FrançoisSavard Espero que você tenha encontrado uma resposta para sua pergunta desde que publicou esse comentário - mas aqui está uma resposta, apenas por uma questão de perfeição: ainda é perfeitamente aceitável java.sql.Date com PreparedStatementetc! Mas quando você está distribuindo, use um LocalDateque você converta usando java.sql.Date.valueOfe java.sql.Date.valueOfao 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.
gustafc
19

O único momento para usar java.sql.Dateé em PreparedStatement.setDate. Caso contrário, use java.util.Date. Está dizendo que ResultSet.getDateretorna a, java.sql.Datemas pode ser atribuído diretamente a java.util.Date.

Paul Tomblin
fonte
3
Ehm, ResultSet # getDate () retorna sql.Date (que estende util.Date).
Esko
3
@ Esko - "Ehm", eu consertei isso antes que você comentasse (e diminuísse o voto).
Paul Tomblin
1
É importante observar que a razão pela qual um java.sql.Date pode ser atribuído a um java.util.Date é porque o primeiro é uma subclasse do segundo.
dj18
2
Por que é "dizer" que a java.sql.Datepode ser atribuído a a java.util.Datequando o primeiro estende o último? Qual é o ponto que você está tentando enfatizar?
Marquês de Lorne
11

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()));
Israelense
fonte
2
não posso obter fuso horário com isso.
precisa saber é o seguinte
4

tl; dr

Não use nenhum.

Nem

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.Datepretende 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 OffsetDateTimeset ZoneOffset.UTCpara 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::setObjectcom JDBC 4.2 ou posterior.

myPreparedStatement.setObject(  , odt ) ;

Recuperar.

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

java.sql.Date

A java.sql.Dateturma 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.Datequal 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.LocalDatepara 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 ) ;

Tabela de tipos de data e hora em Java (herdado e moderno) e no SQL padrão


Sobre java.time

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?

Tabela de qual biblioteca java.time a ser usada com qual versão do Java ou Android

Basil Bourque
fonte
1
Voto positivo por substituir a referência da Praga pelo vírus Corona. Mais compreensível agora.
ankuranurag2
2

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

java.util.Daterepresenta 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.Timee java.sql.Timestampinterfaces.

java.sql.Dateestende 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.Dateinstâ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.Datetais como getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). Como java.sql.Datenão armazena as informações de horário, elas substituem todas as operações de horário java.util.Datee todos esses métodos são lançados java.lang.IllegalArgumentExceptionse invocados como evidentes nos detalhes de implementação.

Abdul Alim Shakir
fonte