2016-03-15 85 views
2

我已經閱讀了許多關於類似問題的答案,但仍然無法理解整個邏輯。 我的目標是在我的應用程序內部處理日期(包括時間,不僅僅是年份等),並考慮夏時制轉換和服務器的時區。我使用hibernate和postgresql db。我的錯誤是對來自數據庫的所有日期使用 java.util.Date類型。不久我意識到它不包含時區信息。現在問題出現了,如何使用正確的日期(考慮日光和時區)?Java從數據庫映射到休眠映射的日期(時區問題和夏令時)

什麼是正確的方法來做到這一點?使用不同類型的類如Calendar?或者就這樣離開它,但是在我使用日期的每個地方,我應該使用日曆將它轉換爲本地時區。

讓我困惑的是數據如何來自數據庫到Date對象,如果它的安全將日期對象轉換爲日曆以包含時區等?

回答

2

java.util.Date不帶真正的時區信息。按照慣例,它是一個UTC時間戳(好吧,這不是因爲Javadoc狀態跳過了leap seconds)。

但是,如果您認爲它是UTC,那麼至少在Calendar.getTime()方法中您確實很好。

在構建/創建Date實例的應用程序邏輯中使用Calendar而不是Date是很好的第一步。使用日曆方法setTimeZone來管理應用程序中的時區,它將爲您重新計算字段值,如HOUR_OF_DAY

Hibernate使用JDBC來執行SQL語句,因此它綁定到JDBC API的可能性以及您使用的JDBC驅動程序的行爲並導致數據庫的可能性。

我的經驗是,JDBC驅動程序和數據庫確實在日期/時間處理方面有所不同,尤其是當涉及到時區時。例如,postgres sql具有帶和不帶時區的日期/時間字段類型。不幸的是,帶時區的類型不屬於SQL規範的一部分,因此它們不屬於JDBC規範。所以Hibernate不支持它們。

簡短解答的簡短解釋: 解決此問題的最常見方法是確保始終使用相同的時區來存儲和加載來自Hibernate的數據。一個簡單的方法是使用UTC時區,因爲Calendar.getTime()方法總是將當前時間返回爲UTC Date實例,您可以直接將其傳遞給Hibernate。 從數據庫裝載值,使用Calendar.setTime(Date)方法將它們轉換爲Calendar

+0

如果我不得不在服務器上使用UTC,我將顯式設置日曆時區,然後使用Calendar.setTime(Date)也可以嗎? – maximus

+0

如果你想在一個非UTC時區有'Date'內容,你應該首先使用帶有UTC的'Calendar.getInstance(TimeZone)'創建一個'Calendar',然後'setTime(Date)',然後將它改爲任何你喜歡使用'setTimeZone(TimeZone)'的時區。這是否符合你的問題? – Alexander

+0

只是爲了確認,我留下了現在使用的hibernate實體代碼(使用java.util.Date),但是在那裏我使用了那個日期,我只是將它們轉換成Calendar來描述它的方式嗎? – maximus

1

每當您讀取/寫入數據庫時​​,確保您的日期(及其時區)一致的一種方法是確保您在數據庫中使用UTC時區。如前所述,JDBC存在一個常見問題,因爲當它從數據庫中讀取日期時,默認情況下會將日期視爲本地JVM時區。因此,如果您的本地JVM時區與數據庫中使用的時區不同,您將面臨意外的時區轉換(例如,在不同時區中的兩個位置運行應用程序時)。

有一篇很好的文章解釋了問題的根源here。還有一個小型的開源庫DbAssist,你可以在你的項目中使用它來確保JDBC和Hibernate將數據庫中的日期視爲UTC時區。

要包括修復,添加下面的依賴關係到您的POM文件:

<dependency> 
    <groupId>com.montrosesoftware</groupId> 
    <artifactId>DbAssist-5.2.2</artifactId> 
    <version>1.0-RELEASE</version> 
</dependency> 

這種特殊的修補程序適用於Hibernate的5.2.2版本,但如果你使用任何其他的Hibernate,只是參照本link爲適當的修復版本。它還包括安裝指南,根據您是使用HBM文件還是JPA註釋(帶或不帶Spring Boot)映射您的實體字段而有所不同。在這兩種情況下,應用修復只是一行code的問題。

這個庫(修復)的好處是你不必改變實體類中的任何東西。一旦添加了依賴關係並應用修復,實體中的java.util.Date字段就被認爲是UTC時區(從DB寫入/讀取時)。