隨著時間的推移,當前的事態
自從 JDBC 被發明並對其接口進行標準化以來,已經過去了 20 年,在此期間發生了很多變化。
首先,世界已經全球化,現在一台服務器可以為來自世界各地的用戶提供服務。網速上來了。因此,另一種數據類型被添加到 SQL 中以處理時間。現在類型看起來像這樣:
- DATE - 存儲日期:年、月、日。
- TIME - 存儲時間:小時、分鐘、秒。
- TIMESTAMP - 存儲特定時間點:日期、時間和毫秒。
- TIMESTAMP WITH TIME ZONE - 時間戳和時區(時區名稱或偏移量)。
其次,Java 引入了用於全局時間管理的 DateTime API。它有以下類:
- 日期和時間:
- 本地日期
- 當地時間
- 確切時刻:
- java.time.Instant
- java.time.LocalDateTime
- java.time.OffsetDateTime
- java.time.ZonedDateTime
- 帶時區的時間:
- java.time.OffsetDateTime
- java.time.ZonedDateTime
第三個有趣的點是許多 SQL 客戶端希望從已經在其本地區域中的服務器接收時間。當然,你可以即時轉換時間,但不方便,而且會出錯。
例如,我想從數據庫中獲取今天的所有任務。SQL Server為此提供了一個CURDATE()函數。只有這裡服務器在美國,而我在日本。我希望他返回“我的今天”而不是“他的今天”的所有記錄。
一般來說,SQL服務器還必須能夠與不同時區的客戶端進行智能協作。
現代問題需要現代解決方案
原則上,可以方便地映射來自 Java DateTime API 的新類型和來自 SQL 的新類型。要在 Java 中表示DATE類型,您需要使用JDK 8 DateTime API 中的java.time.LocalDate類。
數據庫中的 TIME 類型可以用 Java 中的兩種類型表示:java.time.LocalTime和java.time.OffsetTime。也沒什麼複雜的。
一個特定的時間點,在數據庫中用TIMESTAMP類型來表示,在Java中可以用4種類型來表示:
- java.time.Instant
- java.time.LocalDateTime
- java.time.OffsetDateTime
- java.time.ZonedDateTime
最後,TIMESTAMP WITH TIME ZONE可以用兩種類型表示:
- java.time.OffsetDateTime
- java.time.ZonedDateTime
由於您已經熟悉 DateTime API,因此記住這件事對您來說並不困難 :)
我會以表格的形式寫出來,這樣會更容易:
類型 | Java類型 |
---|---|
日期 | java.time.LocalDate |
時間 | java.time.LocalTime java.time.OffsetTime |
時間戳 | java.time.Instant java.time.LocalDateTime java.time.OffsetDateTime java.time.ZonedDateTime |
帶時區的時間戳 | java.time.OffsetDateTime _ |
獲取日期
我有一個好消息要告訴你。很長一段時間以來第一次。我們可以繞過getDate()方法的限制,它返回一個 java.sql 日期類型。
重點是對象結果集還有另一個有趣的方法 - getObject()。此方法有兩個參數:一個列和一個類型,並返迴轉換為給定類型的列的值。該方法的一般形式如下:
ClassName Name = getObject(column, ClassName);
而如果你想將DATE類型轉換為java.time.LocalDate類型,那麼你需要這樣寫:
LocalDate localDate = results.getObject(4, LocalDate.class);
一般而言,任何 TIMESTAMP 都可以轉換為多種類型:
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);
重要的! 如果您有過時的 MySQL JDBC 驅動程序,此代碼將不起作用。注意在你的 pom.xml 中編寫的“mysql-connector-java”的版本,或者添加到項目設置中的庫中。
順便說一句,以同樣的方式,您可以解決無法為基本類型存儲 null 的問題。如果表列的類型為 INT,則有幾種方法可以從中獲取空值。請參見下面的示例:
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
MySQL 中的時區設置
MySQL 也發生了很多有趣的事情。如您所知,在創建 MySQL 連接時,您可以向其添加各種參數:mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning
因此,添加了三個參數來處理 MySQL 中的時區。您可以在與服務器建立連接時傳遞這些參數。
下面我將與他們一起給出一個表格:
範圍 | 價值觀 | 默認值 |
---|---|---|
連接時區 | 本地 | 服務器 | 用戶區 | 服務器 |
forceConnectionTimeZoneToSession | 真 | 錯誤的 | 真的 |
保存瞬間 | 真 | 錯誤的 | 錯誤的 |
使用connectionTimeZone參數,我們選擇將執行所有請求的時區(time zone)。從客戶端的角度來看,服務器正在指定的時區運行。
forceConnectionTimeZoneToSession參數導致會話 time_zone 變量被忽略並替換為 connectionTimeZone。
最後,preserveInstants參數控制 JVM 的 timeZone 和 connectionTimeZone 之間的精確時間轉換。
最常見的配置是:
-
connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=false - 對應於 useLegacyDatetimeCode=true 的舊 MySQL JDBC 驅動程序版本 5.1。
-
connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=true是一種新模式,它提供了處理日期和時間值的最自然方式。
-
connectionTimeZone=SERVER & preserveInstants=true - 對應於 useLegacyDatetimeCode=false 的舊 MySQL JDBC 驅動程序版本 5.1。
-
connectionTimeZone=user_defined & preserveInstants=true - 幫助克服連接器無法識別服務器時區的情況,因為它被設置為通用縮寫,例如 CET/CEST。
是的,日期是一個有趣的話題,並且有很多問題。俗話說:當然很可怕,但我也不生氣!:)
GO TO FULL VERSION