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

MySQL驱动中关于时间的坑

nanshan 2024-11-21 18:48 8 浏览 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驱动中关于时间的坑

相关推荐

小白初学linux之无法修改系统分辨率

/*此文是做为自己的一个总结还有就是最好也可以给大家提供一些帮助。*/时间:2020年7月14日11:28:41我安装的是Ubuntu20.04LTS,昨天处理的是,grub的引导问题,因为是...

Ubuntu 如何启动、停止或重启服务

在本文中,我们向您介绍在Ubuntu中启动、停止和重启服务的方法。列出Ubuntu中的所有服务在开始之前,先获取计算机上所有服务的列表,因为我们需要知道服务名称来管理服务。service--...

Win11学院:如何在Windows 11上使用WSL安装Ubuntu

IT之家2月18日消息,科技媒体pureinfotech昨日(2月17日)发布博文,介绍了3中简便的方法,让你轻松在Windows11系统中,使用WindowsSubs...

Linux安装中文输入法-Google拼音输入法,搜狗输入法

主要步骤,选择适合自己的尝试:1)卸载之前没装好的搜狗输入法。@:~/Downloads$sudoapt-getremovefcitx*删除依赖库@:~/Downloads$sudoap...

Ubuntu 22.04 请谨慎使用搜狗输入法,可能是你当机原因

在Ubunutu下没有什么有名的输入法,也就听说搜狗输入法有Linux版本,所以特意到官网去找了下载。在Ubuntu新版本里,他仍然用的是fcitx框架的输入引擎,而不是默认的ibus,所以要先把i...

前钢后胶!徐工XMR403VT小型压路机有点意思

【第一工程机械网原创】在越来越注重施工品质,对项目管理越来越精细化的今天,施工方在施工设备选择上,也越来越讲究设备的配套分工,因此小型压路机的应用场景也越来越多。徐工XMR403VT小型压路机高度集...

图大明白 | 404错误为什么是Not Found?为什么是404?

“404错误”大家都不陌生吧?常规来讲它长这样或者长这样艺术一点的长这样404NotFound意思就是所请求的页面不存在或者已被删除被称为“互联网最后一个界面”有很多同学发出疑问:为什么是404?...

Nginx负载均衡安全配置说明2(nginx负载均衡部署)

上一节,我们对Nginx安全配置的几个知识点做了一个说明,例如限制IP访问、文件目录禁止访问限制、需要防止DOS攻击、请求方法的限制和限制文件上传的大小这个进行了一个分析说明,详细的文章请关注我的头条...

惊艳写真系列第403期,本期主人公—叶青

惊艳写真系列第403期,本期主人公—叶青制作不易,欢迎各位看官提供宝贵意见。如果您喜欢记得关注,么么哒。您的每一份点赞和关注都是对作者的最大认可(图片素材均来源于网络,如有侵权联系删除。)本篇是写惊艳...

先秦布币之尖足布、圆足布、方足布,今年圆足最高拍卖价16万一枚

在战国魏、韩地区诞生桥足平首布、锐角平首布之后,赵也诞生了尖足平首布,并且在尖足布的基础上,后来相继派生出了圆足布、三孔布,以及类圆足布和类方足布。一尖足布尖足布是从耸肩尖足空首布演变而来的,是黄河...

403 禁止访问错误的全面排查与解决方案

当遇到403Forbidden错误时,意味着服务器已接收并理解请求,但拒绝执行访问操作。以下从用户端、服务器端等多个维度,提供分步排查与解决方法。一、用户端基础排查1.检查URL准确性确认...

这才是2019年夏最高颜值的泳装(2019夏季泳装秀)

最近的天气是越来越热了,又到了暑期泳衣勇闯海滩的时刻了,打开ins,微博满满地都是各大博主晒的泳装照,明星们也纷纷跑到海边去度假了。虽然我们没有超模般地身材,但是到了海边我们也要成为人群中最亮眼的那颗...

朋友圈爆火!这组《衡中班主任的一天》漫画,感动了无数人!

很多人觉得做老师很轻松他们说有些老师一天一节课就下班了有双休,还有寒暑假,真让人羡慕呀······但事实真是这样吗?最近衡水中学的赵心扬同学画了一组漫画形象地还原了衡中班主任一天的生活那么衡中班主任一...

国家安全教育 | 一组漫画,带你走进国家安全!

当前,我国面临哪些安全威胁?下面带你来看一组漫画!①你要配合,注意保密。我绝不对别人讲。②这件事,千万别对别人讲。③咱单位的…喂!老k!你要当心,有风声了!④你的泄密行为已触犯了国家法律!①请你协助了...

400、403、404、405,访问网页时出现这些代码是什么意思?

今天小泽访问一个页面时,出现了403,很抱歉,您的访问请求被禁止的提示。相信经常用电脑访问网页的朋友都遇到过这种情况,有的网页提示错误代码403,有的提示404,那这些代码都代表了什么呢?有什么含义呢...

取消回复欢迎 发表评论: