java.util.Date vs java.sql.Date

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

Parabéns, você acertou meu problema de estimação favorito com o JDBC: manipulação de class de data.

Basicamente, os bancos de dados geralmente suportam pelo menos três formas de campos de data e hora, que são data, hora e registro de data e hora. Cada um deles tem uma class correspondente no JDBC e cada um deles estende java.util.Date . A semântica rápida de cada um desses três é o seguinte:

  • java.sql.Date corresponde a SQL DATE, o que significa que armazena anos, meses e dias, enquanto horas, minutos, segundos e milissegundos são ignorados. Além disso, o sql.Date não está vinculado a fusos horários.
  • java.sql.Time corresponde ao SQL TIME e, como deveria ser óbvio, contém apenas informações sobre horas, minutos, segundos e milissegundos .
  • java.sql.Timestamp corresponde a SQL TIMESTAMP, que é a data exata para o nanossegundo ( note que util.Date suporta apenas 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 atual, mês e dia, etc.

Finalmente: Qual deles usar?

Depende do tipo SQL do campo, na verdade. PreparedStatement tem setters para todos os três valores, #setDate() sendo o único para sql.Date , #setTime() para sql.Time e #setTimestamp() para sql.Timestamp .

Note que se você usar ps.setObject(fieldIndex, utilDateObject); você pode realmente dar um util.Date normal para a maioria dos drivers JDBC que irá devorá-lo feliz como se fosse do tipo correto, mas quando você solicitar os dados depois, você pode perceber que você está realmente faltando coisas.

Eu estou realmente dizendo que nenhuma das datas deve ser usada.

O que estou dizendo é que salve os milissegundos / nanossegundos como longos simples e converta-os em quaisquer objects que você esteja usando ( plug obrigatório de tempo de joda ). Uma maneira hacky que pode ser feita é armazenar o componente de data como um componente longo e temporal como outro, por exemplo agora seria 20100221 e 154536123. Esses números mágicos podem ser usados ​​em consultas SQL e serão portáveis ​​de database para outro e permitirá que você evite essa parte da JDBC / Java Date API: s inteiramente.

Edição tardia: A partir do Java 8, você não deve usar o java.util.Date nem o java.sql.Date se puder evitá-lo, e preferir usar o pacote java.time (baseado no 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 methods / construtores de bibliotecas que o utilizam (como o JDBC). Não o contrário. Você não deseja introduzir dependencies às bibliotecas de database para aplicativos / módulos que não lidam explicitamente com o JDBC.

java.util.Date – ao usar bibliotecas que o usam. Caso contrário, o mínimo possível, por vários motivos:

  • É mutável, o que significa que você tem que fazer uma cópia defensiva dela toda vez que você passá-lo ou devolvê-lo de um método.

  • Ele não lida muito bem com datas, que retrocedem pessoas como a sua, realmente, acho que aulas de manipulação de datas deveriam.

  • Agora, porque o juD não faz seu trabalho muito bem, as horríveis classs do Calendar foram introduzidas. Eles também são mutáveis ​​e difíceis de se trabalhar e devem ser evitados se você não tiver escolha.

  • Existem alternativas melhores, como a API Joda Time ( que pode até mesmo tornar-se o Java 7 e se tornar a nova API de manipulação de data oficial – uma pesquisa rápida diz que não).

Se você sente que é um exagero introduzir uma nova dependência como Joda, não é tão ruim usar para campos de timestamp em objects, embora eu mesmo geralmente os envolva no juD ao passá-los, para segurança de tipos e como documentação.

A única hora de usar o java.sql.Date é em um PreparedStatement.setDate . Caso contrário, use java.util.Date . Está dizendo que ResultSet.getDate retorna um java.sql.Date mas ele pode ser atribuído diretamente a um java.util.Date .

Eu tive o mesmo problema, a maneira mais fácil que encontrei para inserir a data atual em uma declaração preparada é esta:

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

A class java.util.Date em Java representa um momento específico no tempo (e .g., 2013 nov 25 16:30:45 até milissegundos), mas o tipo de dados DATE no DB representa apenas uma data (por exemplo, 25 de novembro de 2013). Para evitar que você forneça um object java.util.Date ao DB por engano, o 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 (então, horas e minutos serão ignorados pelo driver de database). Isso é feito com a class java.sql.Date:

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

Um object java.sql.Date pode armazenar um momento no tempo (de modo que é fácil construir a partir de um java.util.Date), mas lançará uma exceção se você tentar pedir as horas (para impor seu conceito de ser um data apenas). Espera-se que o driver DB reconheça essa class e use apenas 0 para as horas. Tente isto:

 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()); }