做seo怎么设计网站,做网站建设月收入多少,建设局职责,个性化网页设计目录
一、前置准备
1.1 版本号列表
1.2 Sql脚本
1.3 application.yaml配置
1.4 数据库时区设置
二、java Date类型与#xff08;jdbcType#xff09;TIMESTAMP类型的转换
2.1 jdbc对serverTimeZone的处理
2.2 java Date转#xff08;jdbcType#xff09;TIMESTAMP …
目录
一、前置准备
1.1 版本号列表
1.2 Sql脚本
1.3 application.yaml配置
1.4 数据库时区设置
二、java Date类型与jdbcTypeTIMESTAMP类型的转换
2.1 jdbc对serverTimeZone的处理
2.2 java Date转jdbcTypeTIMESTAMP
2.3 jdbcTypeTIMESTAMP转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 propertygmtCreate columngmt_create jdbcTypeTIMESTAMP/
result propertygmtModified columngmt_modified jdbcTypeTIMESTAMP/ 这里指定了jdbcType为TIMESTAMP也就是说不管mysql中是datetime还是timestamp类型对应到jdbcTypef都是TIMESTAMP类型所以研究java时间和 (jdbcType)TIMESTAMP的转换是我们理解时区问题的关键。
一、前置准备
1.1 版本号列表
本文研究所得结论均基于此处列出的软件版本号其他版本号结论或有出入但原理可供参考。 springboot版本号 3.3.5mybatis-plus版本号3.5.7mysql版本号9.0.1mysql驱动版本号com.mysql.cj.jdbc.Driver: 8.2.0jdk版本号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配置
serverTimezoneGMT%2B9即设置连接时区为9时区。
server:port: 8080spring:application:name: TestProgramdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/pds?serverTimezoneGMT%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类型与jdbcTypeTIMESTAMP类型的转换 先说结论Java的Date类型实际上是经过jdbc转换为字符串的形式写入数据库查询时同理以字符串的形式转换为Java Date类型。时区转换时只和jdbcUrl指定的serverTimeZone有关和数据库配置的时区无关。 下面我们进行验证
2.1 jdbc对serverTimeZone的处理 这个处理主要在com.mysql.cj.protocol.a.NativeProtocol#configureTimeZone的方法中进行的。jdbc驱动会从jdbcUrl中取时区设置到serverSession中如果没有则取默认时区。我们断点来看。 首先去拿serverTimezone指定的时区这里获取到的是GMT9时区将serverSession的timeZone设置为9时区需要注意的是如果没有从连接中获取到时区就会用TimeZone.getDefault()获取到应用运行的本地默认时区我们这里是GMT8。
2.2 java Date转jdbcTypeTIMESTAMP 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来执行语句。 注意控制台的打印还是服务器本地时间(GMT8)时间并不是拼好的sql语句中的GMT9时间这个算是个不好的体验吧。。。 最终落库时间为拼好的sql中的时间 2.3 jdbcTypeTIMESTAMP转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的Datenew Date(long mills)同样接收一个时间戳作为参数。 结果验证输出的是8时间 三、结论
Java的Date类型实际上是经过jdbc转换为字符串的形式写入数据库查询时同理以字符串的形式转换为Java Date类型。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点09timestamp类型的时间数据先转成InternalTimestamp类9时区的18点09然后转成时间毫秒值距离1970-01-01最后转成java.sql.Timestamp也就是展示8时间的17点09。 结果展示 和我们预期的一模一样这个案例再次印证了jdbc通过字符串读写且与数据库时区配置无关。 其他本文其他代码全部是mybatisX-Generator生成的过于简单就不贴出源码了。