• 0078 Java与MySQL时间戳传递/存储/协调问题--userLegacyDatetimeCode--userTimezone--serverTimezone


    00. 基本问题

    0.0 版本: 驱动5.1.47和8.0.17
    0.1 MySQL驱动5.1有userLegacyDatetimeCode和userTimezone两个参数, 8.0没有
    0.2 Java与MySQL间传递时间戳的时候, 传递的是年月日时分秒, 没有时区
    0.3 MySQL传递回来的是: MySQL读取到底层存储的时间戳, 按照当前连接(MySQL侧)的时区转为年月日时分秒
    0.4 但是, 两个系统时区可能会不同, userLegacyDatetimeCode和userTimezone就是用来协调时区的

    01. MySQL驱动5.1

    1.1 数据库连接在建立时, 会创建一个Calendar对象保存在连接中, 其中保存了连接创建时的时区, 即下文的"连接时区". 见ConnectionImpl#705
    1.2 如果配置了serverTimezone,则会将其保存到连接中, 即下文的"配置时区". 见ConnectionImpl#1978
    1.3 userLegacyDatetimeCod=true&userTimezone=false, 这是默认情况
    1.3.1 此时对应Java和MySQL时区相同
    1.3.2 Java接收到MySQL传递来的年月日时分秒, 加上"连接时区"创建时间戳java.sql.Timestamp, 见ResultSetImpl#5877和TimeUtil#369
    1.4 userLegacyDatetimeCod=true&userTimezone=true&serverTimezone=GMT%2B6
    1.4.0 userTimezone=true, 必须在userLegacyDatetimeCod=true时才有效
    1.4.1 此时对应二者时区不同
    1.4.2 与3.2相同, 先将年月日时分秒+"连接时区", 创建时间戳
    1.4.3 再进行时区调整, 调整为"配置时区". 见ResultSetImpl#5877和TimeUtil#160
    1.5 userLegacyDatetimeCod=false&serverTimezone=GMT%2B6
    1.5.1 此时对应二者时区不同
    1.5.2 将年月日时分秒+"配置时区"创建时间戳. 见ResultSetImpl#5874
    1.5.3 这也是8.0的处理方式

    02. MySQL驱动8.0

    2.1 8.0没有userLegacyDatetimeCode和userTimezone两个参数
    2.2 一定要配置serverTimezone为MySQL运行的时区. 连接建立时会将这个时区存储到连接中. 见NativeProtocol#2147#2158
    2.3 将年月日时分秒+"配置时区"构造时间戳. 见SqlTimestampValueFactory#100. 这里的cal就是在#68根据"配置时区"创建的

    03. 代码跟踪中的一些关键点

    版本5.1

    连接初始化的过程

    1. ConnectionImpl#1978 "配置时区"
    2. ConnectionImpl#705 将当前时区保存到了数据库连接中

    读取的过程

    1. MyBatis的各个TypeHandler
    2. ByteArrayRow#63 拿到字节数组
    3. ResultSetRow#705 将字节数组转为字符串
    4. ResultSetImpl#5729 将字符串分离为年月日时分秒
    5. ResultSetImpl#5873 对应上文01.5.2
    6. ResultSetImpl#5877 对应上文01.4.2和01.4.3
    7. ResultSetImpl#5317 如果Java要返回的是String, 则会在这里将时间戳转为jvm当前时区下的年月日时分秒

    版本8.0

    1. NativeProtocol#2147#2158 保存"配置时区"
    2. ByteArrayRow#89 拿到数据库返回的字节, 大致相当于 01.5.1的ByteArrayRow#63
    3. MysqlTextValueDecoder#338 解析字节数组, 拿到年月日时分秒并封装为InternalTimestamp
    4. SqlTimestampValueFactory#100 也就是上文02.3
    5. StringValueFactory#94 如果Java要返回的是String, 就直接将InternalTimestamp转为字符串, 不考虑当前系统时区了, 与5.1的第7条有区别
    6. AbstractResultSetRow#78
    7. PropertyKey jdbc url的property的key枚举类, https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html

    04. The server time zone value '???DZ?׼ʱ?' is unrecognized or represents more than one time zone 异常是怎么回事?

    在三种情况下会抛出
    上文01.4/01.5/02.2情况下未配置serverTimezone是都会抛出
    因为, NativeProtocol#2130或者ConnectionImpl#1960拿到数据库的system_time_zone是乱码, 也就是select @@system_time_zone 的值
    所以要配置serverTimezone为数据库运行的时区

    05. 问题

    时间戳传递为什么不是一个数字形式的秒/毫秒呢, 而是一个没有时区的年月日时分秒呢? 还得协调时区, 多复杂呢?

  • 相关阅读:
    Centos7安装
    Nacos启动命令
    SpringCloud与SpringBoot版本冲突导致入住Zookeeper失败
    RabbitMQ延迟机制
    RabbitMQ消息的可靠性
    基于Java的交换机与队列创建
    一、使用RabbitMQ传递对象
    在SpringBoot应用中使用MQ
    在普通的Maven应用中使用MQ
    RabbitMQ工作模式,交换机和队列管理
  • 原文地址:https://www.cnblogs.com/sonng/p/11294609.html
Copyright © 2020-2023  润新知