美文网首页JavaJava高开发MySQL
Java与MySQL时间戳传递/存储/协调问题--userLeg

Java与MySQL时间戳传递/存储/协调问题--userLeg

作者: java高并发 | 来源:发表于2019-08-03 15:57 被阅读5次

    阅读目录

    • [00. 基本问题]
    • [01. MySQL驱动5.1]
    • [02. MySQL驱动8.0]
    • [03. 代码跟踪中的一些关键点]
    • [04. The server time zone value '???DZ?׼ʱ?' is unrecognized or represents more than one time zone 异常是怎么回事?]
    • [05. 问题]

    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. 问题

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



    欢迎工作一到五年的Java工程师朋友们加入JavaQQ群:219571750,群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

    相关文章

      网友评论

        本文标题:Java与MySQL时间戳传递/存储/协调问题--userLeg

        本文链接:https://www.haomeiwen.com/subject/omijdctx.html