O estado atual das coisas ao longo do tempo
Desde a época em que o JDBC foi inventado e suas interfaces foram padronizadas, 20 anos se passaram e, durante esse tempo, muitas coisas mudaram.
Em primeiro lugar, o mundo se tornou global e agora um servidor pode atender usuários de todo o mundo. A velocidade da internet aumentou. Portanto, outro tipo de dado foi adicionado ao SQL para trabalhar com o tempo. Agora os tipos ficam assim:
- DATA - armazena a data: ano, mês, dia.
- TIME - armazena o tempo: horas, minutos, segundos.
- TIMESTAMP - armazena um ponto específico no tempo: data, hora e milissegundos.
- TIMESTAMP WITH TIME ZONE - TIMESTAMP e fuso horário (nome da zona ou deslocamento).
Em segundo lugar, Java introduziu a API DateTime para gerenciamento de tempo global. Possui as seguintes aulas:
- Data e hora :
- LocalData
- Horário local
- Momento exato :
- java.time.Instant
- java.time.LocalDateTime
- java.time.OffsetDateTime
- java.time.ZonedDateTime
- Hora com fuso horário :
- java.time.OffsetDateTime
- java.time.ZonedDateTime
O terceiro ponto interessante é que muitos clientes SQL gostariam de receber tempo do servidor já em sua zona local . Claro, você pode converter o tempo na hora, mas não é conveniente e há erros.
Por exemplo, desejo obter todas as tarefas de hoje do banco de dados. O SQL Server tem uma função CURDATE() para isso. Só aqui o servidor está nos EUA e eu no Japão. E gostaria que ele devolvesse todos os registros do “meu hoje”, e não do “hoje dele”.
Em geral, o servidor SQL também deve ser capaz de trabalhar de forma inteligente com clientes em diferentes fusos horários.
Problemas modernos exigem soluções modernas
Em princípio, novos tipos da API Java DateTime e tipos do SQL podem ser convenientemente mapeados. Para representar o tipo DATE em Java, você precisa usar a classe java.time.LocalDate da API JDK 8 DateTime.
O tipo TIME do banco de dados pode ser representado por dois tipos de Java: java.time.LocalTime e java.time.OffsetTime . Nada complicado também.
Um ponto específico no tempo, representado pelo tipo TIMESTAMP no banco de dados, pode ser representado em Java por 4 tipos:
- java.time.Instant
- java.time.LocalDateTime
- java.time.OffsetDateTime
- java.time.ZonedDateTime
E, finalmente, TIMESTAMP WITH TIME ZONE pode ser representado por dois tipos:
- java.time.OffsetDateTime
- java.time.ZonedDateTime
Como você já está familiarizado com a API DateTime, lembrar desse assunto não será difícil para você :)
Vou escrever em forma de tabela, assim ficará mais fácil:
TIPO SQL | Tipo Java |
---|---|
DATA | java.time.LocalDate |
TEMPO | java.time.LocalTime java.time.OffsetTime |
TIMESTAMP | java.time.Instant java.time.LocalDateTime java.time.OffsetDateTime java.time.ZonedDateTime |
TIMESTAMP COM FUSO HORÁRIO | java.time.OffsetDateTime _ |
Obtendo a data
Eu tenho uma boa notícia para você. Primeiro em muito tempo. Podemos contornar a limitação do método getDate() , que retorna um tipo Java.sql Date.
A questão é que o objetoconjunto de resultadosexiste outro método interessante - getObject() . Este método recebe dois parâmetros: uma coluna e um tipo, e retorna o valor da coluna convertida para o tipo fornecido. A forma geral do método é a seguinte:
ClassName Name = getObject(column, ClassName);
E se você quiser converter o tipo DATE para o tipo java.time.LocalDate , então você precisa escrever algo como:
LocalDate localDate = results.getObject(4, LocalDate.class);
E qualquer TIMESTAMP em geral pode ser convertido em vários tipos:
java.time.Instant instant = results.getObject(9, java.time.Instant.class);
java.time.LocalDateTime local = results.getObject(9, java.time. LocalDateTime.class);
java.time.OffsetDateTime offset = results.getObject(9, java.time. OffsetDateTime.class);
java.time.ZonedDateTime zoned = results.getObject(9, java.time. ZonedDateTime.class);
Importante! Este código não funcionará se você tiver um MySQL JDBC Driver desatualizado . Preste atenção na versão do "mysql-connector-java" escrita em seu pom.xml, ou adicionada nas Bibliotecas nas configurações do projeto.
A propósito, da mesma forma, você pode contornar a incapacidade de armazenar null para tipos primitivos. Se uma coluna da tabela for do tipo INT, existem algumas maneiras de obter nulo dela. Veja exemplo abaixo:
Integer id1 = results.getObject(8, Integer.class); // this will work
Integer id2 = results.getObject(8, int.class); //this will also work
int id3 = results.getObject(8, Integer.class); //method will return null, JVM will throw NPE
int id4 = results.getObject(8, int.class); //method will return null, JVM will throw NPE
Configuração de fuso horário no MySQL
Muitas coisas interessantes aconteceram com o MySQL também. Como você sabe, ao criar uma conexão MySQL, você pode adicionar vários parâmetros a ela :mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning
Então, três parâmetros foram adicionados para trabalhar com fusos horários no MySQL. Você pode passar esses parâmetros ao estabelecer uma conexão com o servidor.
Abaixo darei uma tabela com eles:
Parâmetro | valores | Valor padrão |
---|---|---|
connectionTimeZone | LOCAL | SERVIDOR | zona de usuário | SERVIDOR |
forceConnectionTimeZoneToSession | verdadeiro | falso | verdadeiro |
preservarInstantes | verdadeiro | falso | falso |
Utilizando o parâmetro connectionTimeZone , selecionamos o fuso horário (time zone) no qual todas as requisições serão executadas. Do ponto de vista do cliente, o servidor está sendo executado no fuso horário especificado.
O parâmetro forceConnectionTimeZoneToSession faz com que a variável time_zone da sessão seja ignorada e substituída por connectionTimeZone.
Finalmente, o parâmetro preserveInstants controla a conversão de tempo exato entre timeZone e connectionTimeZone da JVM.
As configurações mais comuns são:
-
connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=false - corresponde ao antigo driver MySQL JDBC versão 5.1 com useLegacyDatetimeCode=true.
-
connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=true é um novo modo que fornece a maneira mais natural de lidar com valores de data e hora.
-
connectionTimeZone=SERVER & preserveInstants=true - Corresponde ao antigo driver MySQL JDBC versão 5.1 com useLegacyDatetimeCode=false.
-
connectionTimeZone=user_defined & preserveInstants=true - Ajuda a superar a situação em que o fuso horário do servidor não pode ser reconhecido pelo conector porque é definido como uma abreviação genérica, como CET/CEST.
Sim, as datas são um tema interessante e há muitos problemas com elas. Como diz o ditado: é assustador, claro, mas também não estou chateado! :)
GO TO FULL VERSION