Jak mogę skonfigurować JPA / Hibernate, aby przechowywać datę / godzinę w bazie danych jako strefę czasową UTC (GMT)? Rozważ tę jednostkę JPA z adnotacjami:
public class Event {
@Id
public int id;
@Temporal(TemporalType.TIMESTAMP)
public java.util.Date date;
}
Jeśli data to 2008-luty-03 9:30 czasu pacyficznego (PST), to chcę, aby w bazie danych przechowywany był czas UTC z okresu 2008-luty-03 17:30. Podobnie, gdy data jest pobierana z bazy danych, chcę, aby była interpretowana jako UTC. W tym przypadku 17:30 to 17:30 UTC. Po wyświetleniu będzie sformatowany jako 9:30 czasu PST.
Odpowiedzi:
Dzięki Hibernate 5.2 możesz teraz wymusić strefę czasową UTC, korzystając z następującej właściwości konfiguracyjnej:
Więcej informacji znajdziesz w tym artykule .
źródło
useTimezone=true
w parametrach połączenia. Wtedyhibernate.jdbc.time_zone
zadziała tylko ustawienie właściwościuseLegacyDatetimeCode
na falseZgodnie z moją najlepszą wiedzą, musisz umieścić całą aplikację Java w strefie czasowej UTC (tak, aby Hibernate przechowywał daty w UTC) i będziesz musiał przekonwertować na dowolną żądaną strefę czasową podczas wyświetlania rzeczy (przynajmniej robimy to tą drogą).
Przy starcie wykonujemy:
I ustaw żądaną strefę czasową na DateFormat:
źródło
Hibernate nie zna stref czasowych w datach (ponieważ ich nie ma), ale w rzeczywistości to warstwa JDBC powoduje problemy.
ResultSet.getTimestamp
iPreparedStatement.setTimestamp
obaj twierdzą w swoich dokumentach, że domyślnie przekształcają daty do / z bieżącej strefy czasowej JVM podczas odczytu i zapisu z / do bazy danych.Wymyśliłem rozwiązanie tego problemu w Hibernate 3.5 przez podklasę,
org.hibernate.type.TimestampType
która zmusza te metody JDBC do używania UTC zamiast lokalnej strefy czasowej:To samo należy zrobić, aby naprawić TimeType i DateType, jeśli używasz tych typów. Wadą jest to, że będziesz musiał ręcznie określić, że te typy mają być używane zamiast wartości domyślnych w każdym polu daty w twoich POJO (a także łamie czystą kompatybilność JPA), chyba że ktoś zna bardziej ogólną metodę nadpisywania.
AKTUALIZACJA: Hibernate 3.6 zmienił API typów. W 3.6 napisałem klasę UtcTimestampTypeDescriptor, aby to zaimplementować.
Teraz, gdy aplikacja zostanie uruchomiona, jeśli ustawisz TimestampTypeDescriptor.INSTANCE na wystąpienie UtcTimestampTypeDescriptor, wszystkie sygnatury czasowe będą przechowywane i traktowane jako podane w czasie UTC bez konieczności zmiany adnotacji w POJO. [Jeszcze tego nie testowałem]
źródło
UtcTimestampType
?UtcTimestampType
kompatybilna?Date
lubTimestamp
zależy od sterownika JDBC.Dzięki Spring Boot JPA użyj poniższego kodu w pliku application.properties i oczywiście możesz zmodyfikować strefę czasową według własnego uznania
Następnie w pliku klasy Entity
źródło
Dodanie odpowiedzi, która jest całkowicie oparta na podpowiedziach Shauna Stone'a i zobowiązała się do jej pozbycia się. Chciałem tylko szczegółowo to przeliterować, ponieważ jest to częsty problem, a rozwiązanie jest nieco zagmatwane.
To używa Hibernate 4.1.4.Final, chociaż podejrzewam, że wszystko po 3.6 będzie działać.
Najpierw utwórz obiekt divestoclimb UtcTimestampTypeDescriptor
Następnie utwórz UtcTimestampType, który używa UtcTimestampTypeDescriptor zamiast TimestampTypeDescriptor jako SqlTypeDescriptor w wywołaniu super konstruktora, ale w przeciwnym razie deleguje wszystko do TimestampType:
Wreszcie, po zainicjowaniu konfiguracji Hibernacji, zarejestruj UtcTimestampType jako zastąpienie typu:
Teraz sygnatury czasowe nie powinny zajmować się strefą czasową maszyny JVM w drodze do iz bazy danych. HTH.
źródło
Można by pomyśleć, że Hibernate rozwiązuje ten powszechny problem. Ale nie jest! Jest kilka „hacków”, aby to naprawić.
Jedynym, którego używam, jest przechowywanie daty jako długiej w bazie danych. Więc zawsze pracuję z milisekundami po 1/1/70. Mam wtedy metody pobierające i ustawiające w mojej klasie, które zwracają / akceptują tylko daty. Więc API pozostaje takie samo. Wadą jest to, że mam w bazie danych tęsknoty. Tak więc z SQL mogę robić tylko porównania <,>, = - nie wyszukane operatory daty.
Innym podejściem jest użycie niestandardowego typu mapowania, jak opisano tutaj: http://www.hibernate.org/100.html
Myślę, że właściwym sposobem radzenia sobie z tym jest jednak użycie kalendarza zamiast daty. Za pomocą kalendarza możesz ustawić TimeZone przed utrwaleniem.
UWAGA: Głupiutki stackoverflow nie pozwala mi komentować, więc oto odpowiedź na david a.
Jeśli utworzysz ten obiekt w Chicago:
Hibernate utrzymuje go jako „12/31/1969 18:00:00”. Daty powinny być pozbawione strefy czasowej, więc nie jestem pewien, dlaczego miałaby zostać wprowadzona korekta.
źródło
Calendar
problem z czytaniem, myślę, że Hibernate lub JPA powinny zapewnić jakiś sposób określenia dla każdego mapowania strefy czasowej, na którą Hibernate powinien przetłumaczyć datę, którą odczytuje i zapisuje wTIMESTAMP
kolumnie.Timestamp
ponieważ nie możemy koniecznie ufać sterownikowi JDBC do przechowywania dat jako oczekiwalibyśmy.Działa tu kilka stref czasowych:
Wszystkie te mogą być różne. Hibernate / JPA ma poważną wadę projektową polegającą na tym, że użytkownik nie może łatwo zapewnić, że informacje o strefie czasowej są przechowywane na serwerze bazy danych (co pozwala na odtworzenie poprawnych czasów i dat w JVM).
Bez możliwości (łatwego) przechowywania strefy czasowej za pomocą JPA / Hibernate, informacje są tracone, a po utracie ich konstruowanie staje się kosztowne (jeśli w ogóle możliwe).
Twierdziłbym, że lepiej jest zawsze przechowywać informacje o strefie czasowej (powinno być to ustawienie domyślne), a użytkownicy powinni mieć wtedy opcjonalną możliwość optymalizacji strefy czasowej (chociaż tak naprawdę wpływa to tylko na wyświetlanie, nadal istnieje domniemana strefa czasowa w dowolnej dacie).
Przepraszamy, ten post nie zapewnia obejścia (odpowiedź została udzielona w innym miejscu), ale jest to uzasadnienie, dlaczego zawsze ważne jest przechowywanie informacji o strefie czasowej. Niestety, wydaje się, że wielu informatyków i praktyków programowania argumentuje przeciwko potrzebie stref czasowych po prostu dlatego, że nie doceniają perspektywy "utraty informacji" i tego, jak bardzo utrudnia to takie rzeczy jak internacjonalizacja - co jest bardzo ważne w dzisiejszych czasach, gdy strony internetowe są dostępne przez klientów i ludzi w Twojej organizacji, gdy przemieszczają się po świecie.
źródło
Proszę spojrzeć na mój projekt na Sourceforge, który zawiera typy użytkowników dla standardowych typów daty i czasu SQL, a także JSR 310 i Joda Time. Wszystkie typy próbują rozwiązać problem kompensacji. Zobacz http://sourceforge.net/projects/usertype/
EDYCJA: W odpowiedzi na pytanie Dereka Mahara dołączone do tego komentarza:
„Chris, czy twoje typy użytkowników działają z Hibernate 3 lub nowszym? - Derek Mahar, 7 listopada 2010 o 12:30”
Tak, te typy obsługują wersje Hibernate 3.x, w tym Hibernate 3.6.
źródło
Data nie znajduje się w żadnej strefie czasowej (jest to urząd milisekundowy od określonego momentu w tym samym czasie dla wszystkich), ale bazowe (R) bazy danych zazwyczaj przechowują znaczniki czasu w formacie politycznym (rok, miesiąc, dzień, godzina, minuta, sekunda,. ..), która jest wrażliwa na strefę czasową.
Aby być poważnym, Hibernacja MUSI pozwolić, aby w ramach jakiejś formy mapowania została poinformowana, że data DB jest w takiej a takiej strefie czasowej, aby podczas ładowania lub przechowywania nie przyjmowała własnej ...
źródło
Napotkałem ten sam problem, gdy chciałem przechowywać daty w bazie danych jako UTC i unikać używania
varchar
i jawnychString <-> java.util.Date
konwersji lub ustawiania całej mojej aplikacji Java w strefie czasowej UTC (ponieważ może to prowadzić do innych nieoczekiwanych problemów, jeśli JVM jest współdzielone w wielu aplikacjach).Tak więc istnieje projekt open source
DbAssist
, który pozwala łatwo naprawić odczyt / zapis jako datę UTC z bazy danych. Ponieważ używasz adnotacji JPA do mapowania pól w encji, wszystko, co musisz zrobić, to uwzględnić następującą zależność wpom
pliku Maven :Następnie zastosuj poprawkę (dla przykładu Hibernate + Spring Boot), dodając
@EnableAutoConfiguration
adnotację przed klasą aplikacji Spring. Aby uzyskać instrukcje instalacji innych ustawień i więcej przykładów użycia, po prostu zapoznaj się z githubem projektu .Dobrą rzeczą jest to, że nie musisz w ogóle modyfikować jednostek; możesz zostawić ich
java.util.Date
pola takimi, jakimi są.5.2.2
musi odpowiadać używanej wersji Hibernacji. Nie jestem pewien, której wersji używasz w swoim projekcie, ale pełna lista dostarczonych poprawek jest dostępna na stronie wiki github projektu . Powodem, dla którego poprawka jest inna dla różnych wersji Hibernate, jest to, że twórcy Hibernate zmieniali API kilka razy między wydaniami.Wewnętrznie, poprawka wykorzystuje wskazówki od divestoclimb, Shane i kilku innych źródeł w celu utworzenia niestandardowego
UtcDateType
. Następnie mapuje standardjava.util.Date
z niestandardowym,UtcDateType
który obsługuje całą niezbędną obsługę stref czasowych. Mapowanie typów uzyskuje się za pomocą@Typedef
adnotacji w dostarczonympackage-info.java
pliku.Możesz znaleźć tutaj artykuł , który wyjaśnia, dlaczego takie przesunięcie czasu w ogóle zachodzi i jakie są sposoby jego rozwiązania.
źródło
Hibernate nie pozwala na określanie stref czasowych za pomocą adnotacji ani w żaden inny sposób. Jeśli zamiast daty używasz Kalendarza, możesz zaimplementować obejście problemu przy użyciu właściwości AccessType HIbernate i samodzielnie zaimplementować mapowanie. Bardziej zaawansowanym rozwiązaniem jest zaimplementowanie niestandardowego typu użytkownika w celu zmapowania daty lub kalendarza. Oba rozwiązania są wyjaśnione w moim blogu tutaj: http://www.joobik.com/2010/11/mapping-dates-and-time-zones-with.html
źródło