【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 version | Windows 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
文章源自玩技e族-https://www.playezu.com/746196.html
getConnection文章源自玩技e族-https://www.playezu.com/746196.html
从图中看创建一个数据库连接是个非常重量级的操作,选择一个高效的连接池很重要。与本篇文章主要相关的是图中斜体红色加粗部分。文章源自玩技e族-https://www.playezu.com/746196.html
关注点一文章源自玩技e族-https://www.playezu.com/746196.html
关注跟time_zone相关的几个配置项。
相关类及配置说明文件:PropertyDefinitions、LocalizedErrorMessages.properties。
配置项 | 默认值 | sinceVersion |
---|---|---|
connectionTimeZone | 字符串类型,默认值:null | 3.0.2 |
forceConnectionTimeZoneToSession | 布尔类型,默认值:false | 8.0.23 |
preserveInstants | 布尔类型,默认值:true | 8.0.23 |
关注点二
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
getTimestamp
ResultSet的实现类是:com.mysql.cj.jdbc.result.ResultSetImpl,getTimestamp主要涉及两部分:
- MysqlTextValueDecoder将原始报文字段解析为InternalTimestamp对象
- SqlTimestampValueFactory将InternalTimestamp对象解析为应用使用的Timestamp
MysqlTextValueDecoder
SqlTimestampValueFactory