JDBC-Mysql 时区问题详解

 

目录

一、前置准备

1.1 版本号列表

1.2 Sql脚本

1.3 application.yaml配置

1.4 数据库时区设置

二、java Date类型与(jdbcType)TIMESTAMP类型的转换

2.1 jdbc对serverTimeZone的处理

2.2 java Date转(jdbcType)TIMESTAMP

2.3 (jdbcType)TIMESTAMP转java Date 

三、结论 

四、扩展

4.1 将数据库时区设置为+8

4.2 数据库时间展示

4.3 java代码获取到的时间展示 


        之前对Mysql的两种时间类型有过简单的了解,知道「mysql中有两个时间类型,timestamp与datetime,其中timestamp在存储上是包含时区的,而datetime是不包含时区的字符串形式。而通常应用下所说的时区问题,也指的是Java应用使用了jdbc驱动时,存储和读取的时区不一致的问题」。

        但是mybatis是如何将java的java.util.Date类型转成mysql的datetime, timestamp类型存储到db,同时是如何将db中datetime, timestamp类型的数据转成java的Date类型的,本人并没有进行深入的研究,导致在出现时区问题时不能找到根源,故针对源码深入研究了一下,做此记录,供大家借鉴。

        在探讨时区问题之前,默认大家对GMT时间,UTC时间,时区等概念已经了解。需要注意的是,java的时间在存入db之前,是通过jdbc驱动转成jdbcTyep的TIMESTAMP类型的,同理查询时,也是由jdbcTyep的TIMESTAMP类型转成java时间类型。这个稍后我们会讲到,同样可以在mybatis的mapper.xml文件中得到印证:

<result property="gmtCreate" column="gmt_create" jdbcType="TIMESTAMP"/>
<result property="gmtModified" column="gmt_modified" jdbcType="TIMESTAMP"/>

        这里指定了jdbcType为TIMESTAMP,也就是说,不管mysql中是datetime还是timestamp类型,对应到jdbcTypef都是TIMESTAMP类型,所以研究java时间和 (jdbcType)TIMESTAMP的转换,是我们理解时区问题的关键。

一、前置准备

1.1 版本号列表

本文研究所得结论均基于此处列出的软件版本号,其他版本号结论或有出入,但原理可供参考。

springboot版本号

3.3.5
mybatis-plus版本号3.5.7
mysql版本号9.0.1
mysql驱动版本号com.mysql.cj.jdbc.Driver: 8.2.0
jdk版本号openjdk version "17.0.13"

1.2 Sql脚本

DROP TABLE IF EXISTS `pds_user`;
CREATE TABLE IF NOT EXISTS `pds_user`
(`id`           bigint PRIMARY KEY auto_increment,`name`         varchar(10) NOT NULL COMMENT '姓名',`email`        varchar(30) NOT NULL COMMENT '邮箱',`gmt_create`   datetime    NOT NULL DEFAULT (now()),`gmt_modified` timestamp   NOT NULL DEFAULT (now())
);

1.3 application.yaml配置

serverTimezone=GMT%2B9,即设置连接时区为+9时区。

server:port: 8080spring:application:name: TestProgramdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/pds?serverTimezone=GMT%2B9username: rootpassword: 123456mybatis-plus:mapper-locations: classpath*:mapper/*.xmlconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

1.4 数据库时区设置

SET GLOBAL time_zone = '+7:00';SELECT @@global.time_zone;

二、java Date类型与(jdbcType)TIMESTAMP类型的转换

        先说结论:Java的Date类型实际上是经过jdbc转换为字符串的形式写入数据库,查询时同理以字符串的形式转换为Java Date类型。时区转换时只和jdbcUrl指定的serverTimeZone有关,和数据库配置的时区无关。

        下面我们进行验证:

2.1 jdbc对serverTimeZone的处理

        这个处理主要在com.mysql.cj.protocol.a.NativeProtocol#configureTimeZone的方法中进行的。jdbc驱动会从jdbcUrl中取时区设置到serverSession中,如果没有则取默认时区。我们断点来看。

  1. 首先去拿serverTimezone指定的时区,这里获取到的是GMT+9时区;
  2. 将serverSession的timeZone设置为+9时区;
  3. 需要注意的是,如果没有从连接中获取到时区,就会用TimeZone.getDefault()获取到应用运行的本地默认时区(我们这里是GMT+8)。

2.2 java Date转(jdbcType)TIMESTAMP

jdbc将java的Date根据指定的时区转成时间字符串形式,写入数据库,这一过程和数据库时区配置无关。

测试代码:

        因为是jdbc规范定义的接口,所以我们直接找到java.sql.PreparedStatement接口(稍早一些的同学应该都手写过jdbc的crud吧。。。),发现定义了一个setTimestamp()方法;我们断点到这里,发现会走到com.mysql.cj.jdbc.ClientPreparedStatement实现类的,ClientPreparedStatement#setTimestamp的1728行,会调到com.mysql.cj.NativeQueryBindings#setTimestamp方法,该方法第469行又调到com.mysql.cj.NativeQueryBindValue#setBinding。

 setBinding方法中第153行会初始化valueEncoder。我们发现这个valueEncoder是SqlTimestampValueEncoder类型,

 转到SqlTimestampValueEncoder发现会调用com.mysql.cj.protocol.a.SqlTimestampValueEncoder#getString方法,该方法会指定时间格式化字符串和时区(+9),将date转成string返回。

 

         可以看到,转化成的字符串是17点09,而此时,我本地的时间是+8时区的16点09。(截图延迟...),所以最终setTimestamp()方法会将java的date转成指定时区(此时是+9)的字符串返回

        断点继续往下走,会发现最终走到com.mysql.cj.NativeQueryBindValue#writeAsText方法,

         该方法的参数intoMessage底层是个byteBuffer,而byte根据ascii值转换后会发现,其实是拼好的sql语句。

         这里方法还没有执行完,所以sql语句没有拼完,如果执行完全后,就行拼成最终的sql语句。然后由mysql来执行语句。

        注意:控制台的打印还是服务器本地时间(GMT+8)时间,并不是拼好的sql语句中的GMT+9时间,这个算是个不好的体验吧。。。

        最终落库(时间为拼好的sql中的时间):

 

2.3 (jdbcType)TIMESTAMP转java Date 

jdbc将获取到的数据库时间字符串,根据时区转换成java的Date类型,这一过程和数据库的时区配置无关。

测试代码:

        同理,有setTimestamp方法就有getTimestamp方法,研究jdbc规范的java.sql.ResultSet接口,发现有getTimestamp方法,断点到这里,发现会进到com.mysql.cj.jdbc.result.ResultSetImpl#getTimestamp(int)方法中,

这个方法的943行会调到com.mysql.cj.protocol.a.result.ByteArrayRow#getValue方法,

继续断点下去,会进到com.mysql.cj.protocol.a.MysqlTextValueDecoder#decodeTimestamp方法。从这个类名和方法名我们大概能看出来,这个会将mysql的textValue值解码成timestamp类型。继续看这个方法的89行,会先调getTimestamp方法,创建出一个com.mysql.cj.protocol.InternalTimestamp类(时间为17点09)。

然后再调用createFromTimestamp方法,最终调到com.mysql.cj.result.SqlTimestampValueFactory#localCreateFromTimestamp方法,将InternalTimestamp时间根据+9时区转成当前时间的时间戳,再组装Timestamp对象返回(+8时间)。

 点进去看这个Timestamp,其实是继承自java的Date,new Date(long mills)同样接收一个时间戳作为参数。

 结果验证,输出的是(+8时间):

三、结论 

  1. Java的Date类型实际上是经过jdbc转换为字符串的形式写入数据库,查询时同理以字符串的形式转换为Java Date类型
  2. java代码时间转换的时区,只和jdbcUrl指定的serverTimeZone(本例是+9)有关,和mysql服务器的时区(本例是+7)无关。

四、扩展

        如果将数据库的时区再改成+8,那么数据库显示和java代码获取到的时间会发生怎么的变化?

4.1 将数据库时区设置为+8

SET GLOBAL time_zone = '+8:00';SELECT @@global.time_zone;

4.2 数据库时间展示

        这个我们应该都知道,datetime类型的时间没有变化,timestamp类型的时间从之前+7时区的17点09变成+8时区的18点09

4.3 java代码获取到的时间展示 

        先猜一手结论,dateime类型的时间数据,和之前获取到的一样,展示16点09,timestamp类型的时间数据,先转成InternalTimestamp类(+9时区的18点09),然后转成时间毫秒值(距离1970-01-01)最后转成java.sql.Timestamp,也就是展示+8时间的17点09。

        结果展示:

 和我们预期的一模一样!这个案例再次印证了,jdbc通过字符串读写,且与数据库时区配置无关。

其他:本文其他代码全部是mybatisX-Generator生成的,过于简单,就不贴出源码了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/472158.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LC12:双指针

文章目录 125. 验证回文串 本专栏记录以后刷题碰到的有关双指针的题目。 125. 验证回文串 题目链接&#xff1a;125. 验证回文串 这是一个简单题目&#xff0c;但条件判断自己写的时候写的过于繁杂。后面参考别人写的代码&#xff0c;首先先将字符串s利用s.toLowerCase()将其…

H5页面多个视频如何只同时播放一个?

目录 背景1. 首先介绍下 muted 属性2. 监听播放和暂停操作3. 视频播放完毕后返回桌面&#xff0c;再进入H5页面发现视频封面丢失置灰解决思路&#xff1a; 背景 页面模块同时有个四个视频模块&#xff0c;发现可以同时播放四个视频&#xff0c;但是理想的是每次只播放一个。 …

【环境配置】macOS配置jdk与maven

配置jdk与maven 配置jdk与切换java版本命令 maven安装与配置国内镜像源 用到的命令 # 进入 JDK 安装目录 cd /Library/Java/JavaVirtualMachines# 查看文件 ls ➜ jdk-1.8.jdk jdk-11.jdk# 查看路径 pwd ➜ /Library/Java/JavaVirtualMachines# 打开环境变量配置文件 vi &…

基于汇编语言的贪吃蛇程序

摘要 在我们空闲的时候&#xff0c;我们可以用一些我们学过的知识编一些东西&#xff0c;通过这些东西我们可以学习到汇编语言综合应用程序设计方法&#xff0c;还可以提高汇编语言实际应用能力&#xff0c;充分了解计算机硬件和软件&#xff0c;完成理论到实践的推进过程。这…

Qt文件目录操作

文件目录操作相关类 Qt 为文件和目录操作提供了一些类&#xff0c;利用这些类可以方便地实现一些操作。Qt 提供的与文件和目录操作相关的类包括以下几个&#xff1a; QCoreApplication&#xff1a;用于提取应用程序路径&#xff0c;程序名等文件信息&#xff1b;QFile&#x…

2024 CCF中国开源大会“开源科学计算与系统建模openSCS”分论坛成功举办

11月9日&#xff0c;2024 中国计算机学会&#xff08;CCF&#xff09;中国开源大会“开源科学计算与系统建模openSCS”分论坛在深圳落下帷幕。本次论坛由开源科学计算与系统建模工作委员会、苏州同元软控信息技术有限公司&#xff08;简称“同元软控”&#xff09;、深圳景元数…

基于 PyTorch 从零手搓一个GPT Transformer 对话大模型

一、从零手实现 GPT Transformer 模型架构 近年来&#xff0c;大模型的发展势头迅猛&#xff0c;成为了人工智能领域的研究热点。大模型以其强大的语言理解和生成能力&#xff0c;在自然语言处理、机器翻译、文本生成等多个领域取得了显著的成果。但这些都离不开其背后的核心架…

C++(Day35)

一、学习内容 C绪论 C是C语言的拓充&#xff0c;C包含C的所有属性&#xff0c;换一句话说&#xff0c;C语言的语法在C中都合法。 C语言是面向过程的编程思想。 C语言是面向对象的编程思想。&#xff08;半面向对象&#xff0c;半面向过程&#xff09; 可以说在C中一切皆对象。 …

World of Warcraft [WeakAuras]Barney Raid Kit - Collapsing Star Indicator

https://wago.io/BarneyCS 黄色数字表示需要修的血量。 绿色数字表示停止修血。 红色数字表示修血过量&#xff0c;以及该坍缩星将在大爆炸读条结束前多少秒爆炸。 Numbers in yellow means damage required. Numbers in green means HP is good, dont damage anymore. Numbers…

Elasticsearch retrievers 通常与 Elasticsearch 8.16.0 一起正式发布!

作者&#xff1a;来自 Elastic Panagiotis Bailis Elasticsearch 检索器经过了重大改进&#xff0c;现在可供所有人使用。了解其架构和用例。 在这篇博文中&#xff0c;我们将再次深入探讨检索器&#xff08;retrievers&#xff09;。我们已经在之前的博文中讨论过它们&#xf…

C++清除所有输出【DEV-C++】所有编辑器通用 | 算法基础NO.1

各位小伙伴们&#xff0c;上一期的保留小数位数教学够用一辈子&#xff0c;有不错的点赞量&#xff0c;可我连一个粉丝铁粉都没有&#xff0c;你愿意做我的第一个铁粉吗&#xff1f;OK废话不多说&#xff0c;开始&#xff01; 温故与知心 可能你也学过&#xff0c;且是工作者…

麒麟kysec安全

一、kysec安全框架管理 开启kysec getstatus Copy security-switch --set default Copy 重启系统 reboot Copy 刷新页面&#xff0c;等待几分钟&#xff0c;即可完成文件的扫描。 查看kysec状态 getstatus Copy 切换到管理员身份&#xff08;密码&#xff1a;devuser…

本地 / 网络多绑定用例总结

原文连接&#xff1a;AUTOSAR_EXP_ARAComAPI的7章笔记&#xff08;4&#xff09; 情景设定 在前一节的基础上&#xff0c;假设有类似情景&#xff0c;区别在于服务实例 2 位于与 AP 产品相同以太网的不同 ECU 上&#xff0c;服务消费者及其代理驻留在 AP 产品 ECU 上。因以太网…

Android笔记(三十六):封装一个Matrix从顶部/底部对齐的ImageView

背景 ImageView的scaleType默认显示图片是这样&#xff0c;但是有时候设计稿需求希望图片左右能紧贴着ImageView左右边缘&#xff0c;又不破坏图片的比例&#xff0c;用自带的matrix&#xff0c;centerCrop等都可以满足 但是都会造成图片的某些区域被裁剪了&#xff0c;如果设…

什么是项目完整性管理?

项目完整性管理是一种在项目生命周期中确保项目质量、进度、成本、资源等各方面保持一致性与协调性的系统性方法。它不仅涉及项目的规划与执行&#xff0c;还包括对项目中的各项资源、流程、技术的整合和控制&#xff0c;以保障项目的最终交付质量和效果。随着项目复杂性的提升…

【3D Slicer】的小白入门使用指南四

开源解剖影像浏览工具Open Anatomy Browser使用及介绍 和3D slicer米有太大关系,该工具是网页版影像数据的浏览工具(可以简单理解为网页版的3D slicer) 介绍 ● 开放解剖(OA)浏览器是由神经影像分析中心开发的,基于网络浏览器技术构建的图谱查看器。 ● OA浏览器将解剖模…

如何优化Kafka消费者的性能

要优化 Kafka 消费者性能&#xff0c;你可以考虑以下策略&#xff1a; 并行消费&#xff1a;通过增加消费者组中的消费者数量来并行处理更多的消息&#xff0c;从而提升消费速度。 批量消费&#xff1a;配置 fetch.min.bytes 和 fetch.max.wait.ms 参数来控制批量消费的大小和…

论文 | On Second Thought, Let’s Not Think Step by Step!

概述与研究背景 本文探讨了“零样本链式思维”&#xff08;Zero-shot Chain of Thought, CoT&#xff09;在大语言模型&#xff08;LLM&#xff09;中的应用及其潜在的偏见与有害内容生成风险。论文指出&#xff0c;尽管CoT在多种逻辑推理任务中提高了模型的表现&#xff0c;但…

华为云前台展示公网访问需要购买EIP,EIP流量走向

华为云前台网络&#xff08;VPC,安全组&#xff0c;EIP&#xff09; 1.EIP网段是从哪里划分的&#xff1f; 管理员在后台Service_OM已设置 Service_OM-网络资源-外部网络-创建外部网络基本信息&#xff1a;配置参数&#xff1a;*名称 public*网络类型 LOCAL 不带标签 类似开…

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明 文章目录 4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明前言1. Ribbon 介绍1.1 LB(Load Balance 负载均衡) 2. Ribbon 原理2.2 Ribbon 机制 3. Spring Cloud Ribbon 实现负载均衡算法-应用实例4. 总结&#x…