Como posso configurar JPA / Hibernate para armazenar uma data / hora no banco de dados como fuso horário UTC (GMT)? Considere esta entidade JPA anotada:
public class Event {
@Id
public int id;
@Temporal(TemporalType.TIMESTAMP)
public java.util.Date date;
}
Se a data for 03 de fevereiro de 2008, 9h30, horário padrão do Pacífico (PST), quero o horário UTC de 03 de fevereiro de 2008 às 17h30 armazenado no banco de dados. Da mesma forma, quando a data é recuperada do banco de dados, quero que seja interpretada como UTC. Portanto, neste caso, 17h30 são 17h30 UTC. Quando for exibido, será formatado como 9h30 PST.
Respostas:
Com o Hibernate 5.2, agora você pode forçar o fuso horário UTC usando a seguinte propriedade de configuração:
Para mais detalhes, confira este artigo .
fonte
useTimezone=true
na string de conexão. Então, apenas a configuração da propriedadehibernate.jdbc.time_zone
funcionaráuseLegacyDatetimeCode
como falsoPelo que eu sei, você precisa colocar todo o seu aplicativo Java no fuso horário UTC (para que o Hibernate armazene as datas em UTC), e você precisará converter para qualquer fuso horário desejado quando exibir as coisas (pelo menos nós fazemos isso deste jeito).
Na inicialização, fazemos:
E defina o fuso horário desejado para o DateFormat:
fonte
O Hibernate ignora coisas de fuso horário em Datas (porque não existe), mas é na verdade a camada JDBC que está causando problemas.
ResultSet.getTimestamp
ePreparedStatement.setTimestamp
ambos dizem em seus documentos que transformam as datas de / para o fuso horário JVM atual por padrão ao ler e gravar de / para o banco de dados.Eu encontrei uma solução para isso no Hibernate 3.5 criando uma subclasse
org.hibernate.type.TimestampType
que força esses métodos JDBC a usar UTC em vez do fuso horário local:A mesma coisa deve ser feita para corrigir TimeType e DateType se você usar esses tipos. A desvantagem é que você terá que especificar manualmente que esses tipos devem ser usados em vez dos padrões em todos os campos de Data em seus POJOs (e também quebra a compatibilidade JPA pura), a menos que alguém conheça um método de substituição mais geral.
ATUALIZAÇÃO: Hibernate 3.6 mudou a API de tipos. No 3.6, escrevi uma classe UtcTimestampTypeDescriptor para implementar isso.
Agora, quando o aplicativo é iniciado, se você definir TimestampTypeDescriptor.INSTANCE para uma instância de UtcTimestampTypeDescriptor, todos os carimbos de data / hora serão armazenados e tratados como UTC sem ter que alterar as anotações em POJOs. [Eu não testei isso ainda]
fonte
UtcTimestampType
?UtcTimestampType
compatível?Date
ouTimestamp
é dependente do driver JDBC.Com Spring Boot JPA, use o código abaixo em seu arquivo application.properties e, obviamente, você pode modificar o fuso horário de acordo com sua escolha
Então, em seu arquivo de classe Entity,
fonte
Adicionando uma resposta que é completamente baseada e devida ao desinvestimento com uma dica de Shaun Stone. Só queria explicar em detalhes, pois é um problema comum e a solução é um pouco confusa.
Isso está usando o Hibernate 4.1.4.Final, embora eu suspeite que qualquer coisa após o 3.6 funcionará.
Primeiro, crie UtcTimestampTypeDescriptor de divestoclimb
Em seguida, crie UtcTimestampType, que usa UtcTimestampTypeDescriptor em vez de TimestampTypeDescriptor como SqlTypeDescriptor na chamada do superconstrutor, mas delega tudo para TimestampType:
Finalmente, ao inicializar a configuração do Hibernate, registre UtcTimestampType como uma substituição de tipo:
Agora, os timestamps não devem se preocupar com o fuso horário da JVM em seu caminho de ida e volta para o banco de dados. HTH.
fonte
Você pensaria que este problema comum seria resolvido pelo Hibernate. Mas isso não! Existem alguns "hacks" para acertar.
O que eu uso é para armazenar a data como um longo no banco de dados. Portanto, estou sempre trabalhando com milissegundos após 01/01/70. Eu então tenho getters e setters em minha classe que retornam / aceitam apenas datas. Portanto, a API permanece a mesma. O lado ruim é que tenho longos no banco de dados. Assim, com SQL, posso fazer praticamente apenas <,>, = comparações - não operadores de data extravagantes.
Outra abordagem é usar um tipo de mapeamento personalizado conforme descrito aqui: http://www.hibernate.org/100.html
Acho que a maneira correta de lidar com isso é usar um calendário em vez de uma data. Com o calendário, você pode definir o fuso horário antes de persistir.
NOTA: O estúpido stackoverflow não me deixa comentar, então aqui está uma resposta a David a.
Se você criar este objeto em Chicago:
O Hibernate persiste como "31/12/1969 18:00:00". As datas não devem ter fuso horário, por isso não sei por que o ajuste seria feito.
fonte
Calendar
problema de leitura, acho que o Hibernate ou JPA deve fornecer alguma forma de especificar, para cada mapeamento, o fuso horário para o qual o Hibernate deve traduzir a data de leitura e gravação em umaTIMESTAMP
coluna.Timestamp
vez que não podemos necessariamente confiar no driver JDBC para armazenar datas como que seria de esperar.Existem vários fusos horários em operação aqui:
Tudo isso pode ser diferente. O Hibernate / JPA tem uma deficiência severa de design, pois o usuário não pode garantir facilmente que as informações de fuso horário sejam preservadas no servidor de banco de dados (o que permite a reconstrução de horas e datas corretas na JVM).
Sem a capacidade de (facilmente) armazenar o fuso horário usando JPA / Hibernate, as informações são perdidas e, uma vez que as informações são perdidas, torna-se caro construí-las (se possível).
Eu diria que é melhor sempre armazenar informações de fuso horário (deve ser o padrão) e os usuários devem ter a capacidade opcional de otimizar o fuso horário (embora isso realmente afete apenas a exibição, ainda há um fuso horário implícito em qualquer data).
Desculpe, esta postagem não fornece uma solução alternativa (isso foi respondido em outro lugar), mas é uma racionalização de por que sempre armazenar informações de fuso horário é importante. Infelizmente, parece que muitos cientistas da computação e profissionais de programação argumentam contra a necessidade de fusos horários simplesmente porque não apreciam a perspectiva da "perda de informações" e como isso torna coisas como a internacionalização muito difíceis - o que é muito importante hoje em dia com sites acessíveis por clientes e pessoas em sua organização à medida que se movem ao redor do mundo.
fonte
Por favor, dê uma olhada em meu projeto no Sourceforge, que tem tipos de usuário para tipos de data e hora SQL padrão, bem como JSR 310 e Joda Time. Todos os tipos tentam resolver o problema da compensação. Veja http://sourceforge.net/projects/usertype/
EDITAR: Em resposta à pergunta de Derek Mahar anexada a este comentário:
"Chris, seus tipos de usuário funcionam com Hibernate 3 ou superior? - Derek Mahar 7 de novembro de 2010 às 12:30"
Sim, esses tipos suportam versões do Hibernate 3.x incluindo o Hibernate 3.6.
fonte
A data não está em nenhum fuso horário (é um escritório em milissegundo de um momento definido no mesmo tempo para todos), mas os bancos de dados (R) subjacentes geralmente armazenam carimbos de data / hora em formato político (ano, mês, dia, hora, minuto, segundo,. ..) que é sensível ao fuso horário.
Para falar a sério, o Hibernate DEVE permitir que seja informado em alguma forma de mapeamento que a data do banco de dados está em tal e tal fuso horário, de forma que quando ele carrega ou armazena não assume o seu próprio ...
fonte
Eu encontrei exatamente o mesmo problema quando eu queria armazenar as datas no banco de dados como UTC e evitar o uso de conversões
varchar
explícitasString <-> java.util.Date
, ou configurar meu aplicativo Java inteiro no fuso horário UTC (porque isso poderia levar a outros problemas inesperados, se o JVM for compartilhada entre muitos aplicativos).Portanto, existe um projeto de código aberto
DbAssist
, que permite corrigir facilmente a leitura / gravação como data UTC do banco de dados. Como você está usando Anotações JPA para mapear os campos na entidade, tudo que você precisa fazer é incluir a seguinte dependência em seupom
arquivo Maven :Em seguida, você aplica a correção (para o exemplo Hibernate + Spring Boot) adicionando uma
@EnableAutoConfiguration
anotação antes da classe de aplicativo Spring. Para outras instruções de instalação de configurações e mais exemplos de uso, basta consultar o github do projeto .O bom é que você não precisa modificar as entidades; você pode deixar seus
java.util.Date
campos como estão.5.2.2
deve corresponder à versão do Hibernate que você está usando. Não tenho certeza de qual versão você está usando em seu projeto, mas a lista completa de correções fornecidas está disponível na página wiki do github do projeto . A razão pela qual a correção é diferente para várias versões do Hibernate é porque os criadores do Hibernate mudaram a API algumas vezes entre os lançamentos.Internamente, a correção usa dicas de divestoclimb, Shane e algumas outras fontes para criar um personalizado
UtcDateType
. Em seguida, ele mapeia o padrãojava.util.Date
com o personalizadoUtcDateType
que lida com todo o tratamento de fuso horário necessário. O mapeamento dos tipos é obtido por meio de@Typedef
anotações nopackage-info.java
arquivo fornecido .Você pode encontrar um artigo aqui que explica por que essa mudança de tempo ocorre e quais são as abordagens para resolvê-lo.
fonte
O Hibernate não permite especificar fusos horários por anotação ou qualquer outro meio. Se você usar Calendário em vez de data, poderá implementar uma solução alternativa usando a propriedade AccessType de HIbernate e implementando o mapeamento você mesmo. A solução mais avançada é implementar um UserType personalizado para mapear sua data ou calendário. Ambas as soluções são explicadas em minha postagem do blog aqui: http://www.joobik.com/2010/11/mapping-dates-and-time-zones-with.html
fonte