【全网首发】TimeZone-datetime在JVM时区和MySQL Session时区的转换

玩技站长
玩技站长
管理员, Keymaster
11056
文章
0
粉丝
科技百科评论110字数 784阅读2分36秒阅读模式
摘要Time_zone;&lt:&lt:version&gt:/version&gt:测试代码;getConnection;关注有关time_zone的...
引言

【JVM时区配置-两行代码让我们一帮子人熬了一个通宵】描述了由于代码BUG导致存储到数据库的时间比正常时间少八小时的案例。案例中对于数据库字段类型是datetime和timestamp的时区转换关系进行了描述,本文试图从代码角度描述以下逻辑:文章源自玩技e族-https://www.playezu.com/746196.html

  • JDBC场景下MySQL Session时区如何配置的
  • JDBC场景下datetime类型的数据如何转换的

测试环境 MySQL文章源自玩技e族-https://www.playezu.com/746196.html

配置项说明
MySQL versionWindows MySQL Server 8.0.30.0
time_zone+08:00
system_time_zone
创建测试库create database test;
创建测试表create table datetimetest( dt datetime);

应用信息文章源自玩技e族-https://www.playezu.com/746196.html

java version "1.8.0_341"Java(TM) SE Runtime Environment (build 1.8.0_341-b10)Java HotSpot(TM) Client VM (build 25.341-b10, mixed mode, sharing)复制

pom文章源自玩技e族-https://www.playezu.com/746196.html

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version></dependency>复制

分析过程 测试场景文章源自玩技e族-https://www.playezu.com/746196.html

  • JVM是UTC + 8,MySQL time_zone是UTC + 8,MySQL JDBC Driver配置的是UTC + 0
  • JVM 应用程序原始时间是(UTC + 8):2022-10-16 10:00:00
  • MySQL JDBC Driver发送给MySQL server的时间是:2022-10-16 02:00:00(时间由UTC + 8转换为UTC + 0)
  • MySQL server最终存储的时间为:2022-10-16 02:00:00
  • MySQL JDBC Driver从数据库中查出的时间是:2022-10-16 02:00:00
  • 应用程序最终读取到的时间是:2022-10-16 10:00:00

测试代码文章源自玩技e族-https://www.playezu.com/746196.html

【全网首发】TimeZone-datetime在JVM时区和MySQL Session时区的转换插图文章源自玩技e族-https://www.playezu.com/746196.html

getConnection文章源自玩技e族-https://www.playezu.com/746196.html

【全网首发】TimeZone-datetime在JVM时区和MySQL Session时区的转换插图1
从图中看创建一个数据库连接是个非常重量级的操作,选择一个高效的连接池很重要。与本篇文章主要相关的是图中斜体红色加粗部分。文章源自玩技e族-https://www.playezu.com/746196.html

关注点一文章源自玩技e族-https://www.playezu.com/746196.html

关注跟time_zone相关的几个配置项。
相关类及配置说明文件:PropertyDefinitions、LocalizedErrorMessages.properties。

配置项默认值sinceVersion
connectionTimeZone字符串类型,默认值:null3.0.2
forceConnectionTimeZoneToSession布尔类型,默认值:false8.0.23
preserveInstants布尔类型,默认值:true8.0.23

关注点二

【全网首发】TimeZone-datetime在JVM时区和MySQL Session时区的转换插图2

executeUpdate

PreparedStatement的实现类是:com.mysql.cj.jdbc.ClientPreparedStatement,跟本次文章相关的内容如下:

编码器

在NativeProtocol类初始化的时候会将不同数据类型的编码器注册&初始化:

static Map<Class<?>, Supplier<ValueEncoder>> DEFAULT_ENCODERS = new HashMap<>();static { DEFAULT_ENCODERS.put(BigDecimal.class, NumberValueEncoder::new); DEFAULT_ENCODERS.put(BigInteger.class, NumberValueEncoder::new); DEFAULT_ENCODERS.put(Blob.class, BlobValueEncoder::new); DEFAULT_ENCODERS.put(Boolean.class, BooleanValueEncoder::new); DEFAULT_ENCODERS.put(Byte.class, NumberValueEncoder::new); DEFAULT_ENCODERS.put(byte[].class, ByteArrayValueEncoder::new); DEFAULT_ENCODERS.put(Calendar.class, UtilCalendarValueEncoder::new); DEFAULT_ENCODERS.put(Clob.class, ClobValueEncoder::new); DEFAULT_ENCODERS.put(Date.class, SqlDateValueEncoder::new); DEFAULT_ENCODERS.put(java.util.Date.class, UtilDateValueEncoder::new); DEFAULT_ENCODERS.put(Double.class, NumberValueEncoder::new); DEFAULT_ENCODERS.put(Duration.class, DurationValueEncoder::new); DEFAULT_ENCODERS.put(Float.class, NumberValueEncoder::new); DEFAULT_ENCODERS.put(InputStream.class, InputStreamValueEncoder::new); DEFAULT_ENCODERS.put(Instant.class, InstantValueEncoder::new); DEFAULT_ENCODERS.put(Integer.class, NumberValueEncoder::new); DEFAULT_ENCODERS.put(LocalDate.class, LocalDateValueEncoder::new); DEFAULT_ENCODERS.put(LocalDateTime.class, LocalDateTimeValueEncoder::new); DEFAULT_ENCODERS.put(LocalTime.class, LocalTimeValueEncoder::new); DEFAULT_ENCODERS.put(Long.class, NumberValueEncoder::new); DEFAULT_ENCODERS.put(OffsetDateTime.class, OffsetDateTimeValueEncoder::new); DEFAULT_ENCODERS.put(OffsetTime.class, OffsetTimeValueEncoder::new); DEFAULT_ENCODERS.put(Reader.class, ReaderValueEncoder::new); DEFAULT_ENCODERS.put(Short.class, NumberValueEncoder::new); DEFAULT_ENCODERS.put(String.class, StringValueEncoder::new); DEFAULT_ENCODERS.put(Time.class, SqlTimeValueEncoder::new); DEFAULT_ENCODERS.put(Timestamp.class, SqlTimestampValueEncoder::new); DEFAULT_ENCODERS.put(ZonedDateTime.class, ZonedDateTimeValueEncoder::new);}复制

与datetime相关的是SqlTimestampValueEncoder

SqlTimestampValueEncoder

【全网首发】TimeZone-datetime在JVM时区和MySQL Session时区的转换插图3

getTimestamp

ResultSet的实现类是:com.mysql.cj.jdbc.result.ResultSetImpl,getTimestamp主要涉及两部分:

  • MysqlTextValueDecoder将原始报文字段解析为InternalTimestamp对象
  • SqlTimestampValueFactory将InternalTimestamp对象解析为应用使用的Timestamp

MysqlTextValueDecoder

【全网首发】TimeZone-datetime在JVM时区和MySQL Session时区的转换插图4

SqlTimestampValueFactory

【全网首发】TimeZone-datetime在JVM时区和MySQL Session时区的转换插图5

 
匿名

发表评论

匿名网友
确定

拖动滑块以完成验证