您好,登錄后才能下訂單哦!
這篇文章主要講解了如何使用Java中的mysql時區,內容清晰明了,對此有興趣的小伙伴可以學習一下,相信大家閱讀完之后會有幫助。
前言
話說工作十多年,mysql 還真沒用幾年。起初是外企銀行,無法直接接觸到 DB;后來一直從事架構方面,也多是解決問題為主。
這次搭建海外機房,圍繞時區大家做了一番討論。不說最終的結果是什么,期間有同事認為 DB 返回的是 UTC 時間。
這里簡單做個驗證,順便看下時區的問題到底是如何處理。
環境
openjdk version “1.8.0_242”
mysql-connector-java “8.0.20”
mysql “5.7” 時區 TZ=Europe/London
本地時區 GMT+8
創建個簡單的庫test及表user, 表結構如下:
CREATE TABLE `user` ( `name` varchar(50) NOT NULL, `birth_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=latin1
插入一條測試數據:
mysql> insert into `user` -> values ('Tom', time('2020-05-15 08:00:00')); Query OK, 1 row affected (0.01 sec) mysql> select * from user; +------+---------------------+ | name | birth_date | +------+---------------------+ | Tom | 2020-05-14 08:00:00 | +------+---------------------+ 1 row in set (0.00 sec)
測試代碼:
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root"); Statement stmt = conn.createStatement(); stmt.execute("select * from user where name = 'Tom'"); ResultSet rs = stmt.getResultSet(); while (rs.next()) { Timestamp timestamp = rs.getTimestamp("birth_date"); System.out.println(timestamp.toLocalDateTime().toString()); }
執行結果:
2020-05-14T15:00
分析
程序的執行過程同時用 wireshark 抓了包。可以看到一次查詢,做了這么多次的交互(包含了會話初始化)。這里可以看到 #177 的交互返回查詢的結果:Tom 2020-05-14 08:00:00,與 DB 中的數據相符。可見,返回的并不是 UTC 時間。
在 TCP 抓包結果中 #155 的查詢語句:
/* mysql-connector-java-8.0.20 (Revision: afc0a13cd3c5a0bf57eaa809ee0ee6df1fd5ac9b) */ SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@collation_server AS collation_server, @@collation_connection AS collation_connection, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout, @@license AS license, @@lower_case_table_names AS lower_case_table_names, @@max_allowed_packet AS max_allowed_packet, @@net_write_timeout AS net_write_timeout, @@performance_schema AS performance_schema, @@query_cache_size AS query_cache_size, @@query_cache_type AS query_cache_type, @@sql_mode AS sql_mode, @@system_time_zone AS system_time_zone, @@time_zone AS time_zone, @@transaction_isolation AS transaction_isolation, @@wait_timeout AS wait_timeout;
服務端返回的 time_zone 為 BST。與本地時區的轉換,由 mysql 的 connector 自動完成。
進階
時區自動轉換
實現源碼:
ResultSetImpl源碼
this.defaultTimestampValueFactory = new SqlTimestampValueFactory(pset, null, this.session.getServerSession().getServerTimeZone());@Overridepublic Timestamp getTimestamp(int columnIndex) throws SQLException { checkRowPos(); checkColumnBounds(columnIndex); return this.thisRow.getValue(columnIndex - 1, this.defaultTimestampValueFactory); }
如何確認服務端時區?
使用會話中的服務端時區進行服務端時區。會話初始化時會進行時區的確認,比如前面獲取的到BST。確認時區的邏輯在NativeProtocol#configureTimezone()中:
public void configureTimezone() { #從mysql的響應獲取 time_zone 和 system_time_zone 的設置 String configuredTimeZoneOnServer = this.serverSession.getServerVariable("time_zone"); if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) { configuredTimeZoneOnServer = this.serverSession.getServerVariable("system_time_zone"); } #從 jdbc url 參數 serverTimezone 獲取時區 String canonicalTimezone = getPropertySet().getStringProperty(PropertyKey.serverTimezone).getValue(); if (configuredTimeZoneOnServer != null) { //如果 jdbc url 中未通過 serverTimezone 指定時區。則從TimeZoneMapping.properties中獲取mysql 回傳的時區縮寫對應的標準時區,比如此處的 BST => Europe/London //會出現無法映射的情況,不如 CEST 無法映射到 => Europe/Berlin,可以指定自定義的 Properties 文件進行映射 // user can override this with driver properties, so don't detect if that's the case if (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone)) { try { canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, getExceptionInterceptor()); } catch (IllegalArgumentException iae) { throw ExceptionFactory.createException(WrongArgumentException.class, iae.getMessage(), getExceptionInterceptor()); } } } //如果 jdbc url 中通過 serverTimezone 指定了時區,則優先使用該時區 if (canonicalTimezone != null && canonicalTimezone.length() > 0) { this.serverSession.setServerTimeZone(TimeZone.getTimeZone(canonicalTimezone)); // // The Calendar class has the behavior of mapping unknown timezones to 'GMT' instead of throwing an exception, so we must check for this... // if (!canonicalTimezone.equalsIgnoreCase("GMT") && this.serverSession.getServerTimeZone().getID().equals("GMT")) { throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("Connection.9", new Object[] { canonicalTimezone }), getExceptionInterceptor()); } } }
關于 serverTimezone 的官方說明
Override detection/mapping of time zone. Used when time zone from server doesn't map to Java time zone
修改一下 jdbc url,通過serverTimezone指定時區為 GMT+8:jdbc:mysql://localhost:3306/test serverTimezone=GMT%2B8&useSSL=false
再次執行代碼:
2020-05-14T08:00
看完上述內容,是不是對如何使用Java中的mysql時區有進一步的了解,如果還想學習更多內容,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。