百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

MySQL驱动中关于时间的坑

nanshan 2024-11-21 18:48 10 浏览 0 评论

MySQL驱动中关于时间的坑

背景:MySQL 8.0数据库;

最近在做一个小框架,因为本身比较精简,就没有引入太多依赖,直接用了JDBC来操作数据库,因为我的表中有一个datetime类型的字段,对应的Java代码中使用的是java.time.LocalDateTime,在处理这个日期字段的时候,就遇到了一个有趣的问题;

在我的数据库表建好后,在Java中使用JDBC原生API实现了一个repository,包含一些数据库的操作,因为代码中有java.time.LocalDateTime字段,在使用java.sql.PreparedStatement的时候不确认java.sql.PreparedStatement.setObject(int, java.lang.Object)方法接收java.time.LocalDateTime类型的入参后是否可以正确处理,就写了一小段测试用例来测试,核心代码如下:

1

preparedStatement.setObject(1, LocalDateTime.now());

一行简单的JDBC调用设置参数,参数类型是LocalDateTime,最终发现没有报错,如果一切OK,那么事情到这里就结束了,但是…….

当我到数据库查看数据的时候,发现日期竟然不对,日期存到数据库后竟然少了14个小时,整整14个小时,当时的我真的是….

还好,机智的我马上就醒悟过来了,这肯定是时区问题,我们现在是东8区,比UTC时间是快8小时,存储到数据库后时间比我们现在慢了14个小时,也就是比UTC时间慢了6个小时,而这正好处于CST时区,CST时区中的代表地区就是北美中部,难道我的服务器人被搬到了北美?

那当然不是了,应该是MySQL的时区设置有问题,MySQL数据库是美国开发的软件,默认情况下时区是CST很符合逻辑,而在我登录到服务器上之后,查看服务器时区,服务器的时区是对的,然后又查看了MySQL的默认时区,果然,我发现MySQL的默认时区设置是CST,使用如下代码将其修改为UTC+8后就正常了:

1

set global time_zone = '+8:00';

2

flush privileges;

3


4

# 使用上述命令修改完毕后使用下面的命令查看修改是否成功

5

show variables where Variable_name like "%time_zone%";

如果事情到这里就结束了,那这也太简单了,而且跟标题似乎没有半毛钱关系呀?别急,其实在执行上一步修改时区之前,机智的我忽然想起来我还有另外一个项目,也用到了java.time.LocalDateTime,并且使用的测试数据库与我当前这个项目是同一个,但是那个项目从来没有发现过这个问题,难道是因为那个项目中使用了mybatis,mybatis对此做了什么特殊处理? 想到这里,笔者赶紧去翻了下mybatis相关源码,发现mybatis对于LocalDateTime也是直接调用了java.sql.PreparedStatement.setObject(int, java.lang.Object)方法将LocalDateTime传给了MySQL驱动,没有做任何处理,那怎么就没问题呢?

既然想不明白,那就debug一下吧,看下在我们JDBC版本的项目中debug LocalDateTime参数是如何传输到MySQL的,最终debug到如下关键源码:

1

代码在com.mysql.cj.ClientPreparedQueryBindings.setTimestamp(int, java.sql.Timestamp)处:

2


3

// PS:这里x是Timestamp类型的,在上层调用时将LocalDateTime转换为了Timestamp类型,这个不影响我们的分析

4

setTimestamp(parameterIndex, x, this.session.getServerSession().getDefaultTimeZone());

在上述代码中this.session.getServerSession().getDefaultTimeZone()最终返回了MySQL数据库中的默认时区,因为我们MySQL中默认时区是CST,所以这里也符合我们观察到的现象,最终保存到数据库的日期比实际我们当前的东8区慢14个小时,但是为什么使用mybatis的另外一个项目就没有问题呢,要知道这两个项目使用的测试数据库是同一个的,这就很奇怪,到这里,我决定再在另一个项目中也debug一下,看下问题到底出在了哪里;

然后我就在使用mybatis版本的项目中做了一个插入测试,然后debug看是怎么处理的,最终debug到如下源码处:

1

代码在com.mysql.cj.ClientPreparedQueryBindings#setLocalDateTime处,关键代码如下:

2


3

formatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 6, true)

4

.toFormatter();

5

sb = new StringBuilder("'");

6

sb.append(x.format(formatter));

7

sb.append("'");

8

setValue(parameterIndex, sb.toString(), targetMysqlType);

可以看到这里并没有使用MySQL数据库的时区信息,所以肯定不会出错,但是这怎么跟没有使用mybatis的项目行为不一致呢?

就在这时,我忽然瞥见了IDEA上方tab标签上有两个ClientPreparedQueryBindings文件,如下所示(这里是给其他不必要的文件关了后的效果,为了更好看出效果):

然后我瞬间明白了,这个问题与mybatis没有任何关系,是两个项目的MySQL驱动不一致导致的,使用mybatis的项目使用的MySQL驱动版本是8.0.26,其中日期处理没有去使用MySQL服务端的时区,所以使用mybatis的项目存储到MySQL中的日期是没问题的,而我当前这个直接使用JDBC项目的MySQL驱动版本是8.0.11,在这个版本的驱动中对于LocalDateTime的处理使用了MySQL服务端的时区,这就导致了存储到MySQL中日期变了;

最后,这个问题到这里有两个最简单的方案解决,一个是修改数据库默认时区,还有一个就是升级使用MySQL8.0.26版本的驱动,而我使用了第三种更复杂一点儿的方案:

1

1、将数据库默认时区修改为东8区;

2

2、将项目的MySQL驱动升级为8.0.26版本;

3

3、对LocalDateTime提前处理,在我自己的代码中将其格式化为字符串,然后调用`java.sql.PreparedStatement.setObject(int, java.lang.Object)`将其设置进去;

为什么使用这种方案呢?

  • 1、将数据库默认时区修改为东8区防止其他人在出相同问题,也防止在其他地方有用到这个时区数据时出错;
  • 2、将项目的MySQL驱动升级为8.0.26是因为其他项目已经在用这个版本的驱动了,尽量保持一致防止再出一些其他兼容性问题;
  • 3、对LocalDateTime提前处理,在自己的代码中将其格式化为字符串而不是等待MySQL驱动去格式化他,防止未来某天升级MySQL驱动时MySQL驱动行为再次修改导致出现问题;

最后,建议大家没有必要不要随意升级jar包,特别是这种涉及底层驱动的jar包,防止出现兼容性问题;
























原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: MySQL驱动中关于时间的坑

相关推荐

如何为MySQL服务器和客户机启用SSL?

用户想要与MySQL服务器建立一条安全连接时,常常依赖VPN隧道或SSH隧道。不过,获得MySQL连接的另一个办法是,启用MySQL服务器上的SSL封装器(SSLwrapper)。这每一种方法各有其...

Mysql5.7 出现大量 unauthenticated user

线上环境mysql5.7突然出现大量unauthenticateduser,进mysql,showprocesslist;解决办法有:在/etc/hosts中添加客户端ip,如192.16...

MySQL 在 Windows 系统下的安装(mysql安装教程windows)

更多技术文章MySQL在Windows系统下的安装1.下载mysql和Framework链接链接:百度网盘请输入提取码提取码:6w3p双击mysql-installer-communit...

MySql5.7.21.zip绿色版安装(mysql数据库绿色版安装)

1、去网上下载满足系统要求的版本(mysql-5.7.21-winx64.zip)2、直接解压3、mysql的初始化(1)以管理员身份运行cmd,在mysql中的bin目录下shift+右键-在...

MySQL(8.0)中文全文检索 (亲测有效)

在一堆文字中找到含有关键字的应用。当然也可以用以下语句实现:SELECT*FROM<表名>WHERE<字段名>like‘%ABC%’但是它的效率太低,是全盘扫描。...

新手教程,Linux系统下MySQL的安装

看了两三个教程。终于在哔哩哔哩找到一个简单高效的教程,成功安装,up主名叫bili逍遥bili,感兴趣可以去看看。下面这个是我总结的安装方法环境:CentOS764位1.下载安装包,个人觉得在...

麒麟服务器操作系统安装 MySQL 8 实战指南

原文连接:「链接」Hello,大家好啊,今天给大家带来一篇麒麟服务器操作系统上安装MySQL8的文章,欢迎大家分享点赞,点个在看和关注吧!MySQL作为主流开源数据库之一,被广泛应用于各种业务...

用Python玩转MySQL的全攻略,从环境搭建到项目实战全解析

这是一篇关于“MySQL数据库入门实战-Python版”的教程,结合了案例实战分析,帮助初学者快速掌握如何使用Python操作MySQL数据库。一、环境准备1.安装Python访问Pytho...

安装MySQL(中标麒麟 安装mysql)

安装MySQL注意:一定要用root用户操作如下步骤;先卸载MySQL再安装1.安装包准备(1)查看MySQL是否安装rpm-qa|grepmysql(2)如果安装了MySQL,就先卸载rpm-...

Mysql最全笔记,快速入门,干货满满,爆肝

目录一、MySQL的重要性二、MySQL介绍三、软件的服务架构四、MySQL的安装五、SQL语句六、数据库相关(DDL)七、表相关八、DML相关(表中数据)九、DQL(重点)十、数据完...

MAC电脑安装MySQL操作步骤(mac安装mysqldb)

1、在官网下载MySQL:https://dev.mysql.com/downloads/mysql/根据自己的macOS版本,选择适配的MySQL版本根据自己需求选择相应的安装包,我这里选择macO...

mysql主从(mysql主从切换)

1、本章面试题什么是mysql主从,主从有什么好处什么是读写分离,有什么好处,使用mycat如何实现2、知识点2.1、课程回顾dubboORM->MVC->RPC->SOApro...

【linux学习】以MySQL为例,带你了解数据库

做运维的小伙伴在日常工作中难免需要接触到数据库,不管是MySQL,mariadb,达梦还是瀚高等其实命令都差不多,下面我就以MySQL为例带大家一起来了解下数据库。有兴趣的小伙伴不妨评论区一起交流下...

玩玩WordPress - 环境简介(0)(玩玩网络科技有限公司)

简介提到开源博客系统,一般都会直接想到WordPress!WordPress是使用PHP开发的,数据库使用的是MySQL,一般会在Linux上运行,Nginx作为前端。这时候就需要有一套LNMP(Li...

服务器常用端口都有哪些?(服务器端使用的端口号范围)

下面为大家介绍一下,服务器常用的一些默认端口,以及他们的作用:  21:FTP服务所开放的端口,用于上传、下载文件。  22:SSH端口,用于通过命令行模式远程连接Linux服务器或vps。  23:...

取消回复欢迎 发表评论: