與時間一起工作

開放

隨著時間的推移,當前的事態

自從 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.LocalTimejava.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。

是的,日期是一個有趣的話題,並且有很多問題。俗話說:當然很可怕,但我也不生氣!:)

留言
  • 受歡迎
你必須登入才能留言
此頁面尚無留言