1. 首页
  2. IT资讯

一次 Jar 包升级引发的血案 & 解决

“u003Cdivu003Eu003Cpu003E转载:https:u002Fu002Fmp.weixin.qq.comu002Fsu002Fyy42Ix2QX9222d8VD3wg0Au003Cu002Fpu003Eu003Cpu003E公众号: javaaduu003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E写在前面u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E最近工作中遇到两例mysql时间戳相关的问题,一个是mysql-connector-java和msyql的精度不一致导致数据查不到;另一例是应用服务器时区错误导致数据查询不到。u003Cu002Fpu003Eu003Cpu003E通过这篇文章,希望能够解答关于mysql中时间戳的几个问题:u003Cu002Fpu003Eu003Colu003Eu003Cli class=”ql-align-justify”u003Emysql中的DATETIME精度为什么只支持到秒?u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003Emysql中的DATETIME类型跟时区有关吗?u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003Emysql设计表的时候,表示时间的字段改如何选择?u003Cu002Fliu003Eu003Cu002Folu003Eu003Cpu003Eu003Cstrongu003E案例分析:DATETIME的精度问题u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E前段时间,将负责的应用的mysql-connector-java的版本从5.1.16升级到5.1.30,在做功能回归的时候发现,使用了类似上面的SQL的用例的运行时数据会有遗漏,导致功能有问题。u003Cu002Fpu003Eu003Cpu003E考虑到我负责的应用中,有个功能需要用到类似下面这种SQL,即使用时间戳作为查询的条件,查询在某个时间戳之后的所有数据。u003Cu002Fpu003Eu003Cp class=”ql-align-center”u003Eu003Cbru003Eu003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F51de53a69fe04ff2ab16e6955d61a702″ img_width=”1080″ img_height=”377″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E经过排查发现:mysql-connector-java在5.1.23之前会将秒后面的精度丢弃再传给MySQL服务端,正好我们使用的mysql版本中DATETIME的精度是秒;在我将mysql-connector-java升级到5.1.30后,从java应用通过mysql-connector-java将时间戳传到MySQL服务端的时候,就不会将毫秒数丢弃了,从mysql-connector-java的角度看是修复了一个BUG,但是对于我的应用来说却是触发了一个BUG。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E如果你面对这个问题,你会怎么修复呢?u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E我们当时想了三种方案:u003Cu002Fpu003Eu003Culu003Eu003Cli class=”ql-align-justify”u003E将mybatis的Mapper接口中的时间戳参数的类型,从java.util.Date改成java.sql.Date;u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003E在传入Mapper接口之前,将传入的时间戳按秒取正,代码如下u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F52ea307652874b3fa93ac09de0c8b177″ img_width=”1080″ img_height=”402″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cp class=”ql-align-justify”u003E在查询之前,将传入的时间戳减1秒;u003Cu002Fpu003Eu003Cpu003E经过验证,方案1会,java.util.Date转过去的java.sql.Date对象会将日期之后的精度全部丢掉,从而导致查询出更多不必要的数据;方案3是可以的,就是可能会查出多一两条数据;方案2也是可以的,相当于从代码上对mysql-connector-java的特性做了补偿。最终我选择的是方案2。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E案例复现u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E利用homebrew安装MySQL,版本是8.0.15,装好后建一个表,用来存放用户信息,SQL如下:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002F8c2ff082d540483a9ff6566b699db02d” img_width=”1080″ img_height=”387″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E使用spirngboot + mybatis作为开发框架,定义一个用户实体,代码如下所示:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Fcdfa119cbc994980b3cb46a4ab342630″ img_width=”1080″ img_height=”884″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E定义该实体对应的Mapper,代码如下:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F183bb7d5b9e3451a94b8a9328888dd02″ img_width=”1080″ img_height=”861″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E设置连接mysql相关的配置,代码如下:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Fb14e3ab0fe4741019078e80e92a68e87″ img_width=”1080″ img_height=”242″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E编写测试代码,先插入一条数据,然后用时间戳作为查询条件去查询,代码如下:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002Fa6851fc885e14c66a29c261b68bc4b98″ img_width=”1080″ img_height=”1128″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E运行单测,如我们的设想,确实是没有查询出数据来,结果如下:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F09d7f62ed0114aafab79eaf1bcf15791″ img_width=”1080″ img_height=”296″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E然后修改代码,利用上面的代码将查询的时间戳按秒取正,代码如下:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002F0e8789a1452840aea568e13a76a85166″ img_width=”1080″ img_height=”938″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E再次运行单测,如我们的设想,这次可以查询出数据来了。u003Cu002Fpu003Eu003Cpu003E不过,这里有个小插曲,我在最开始设计表的时候,使用的SQL语句是下面这样的:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002F42ae3309cb434b0682d3d2a25f343f90″ img_width=”1080″ img_height=”387″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E你一定发现了,这里的datetime已经支持小数点后更小的时间精度了,最多支持6位即最多可以支持到微妙级别。这个特性是什么时候引入的呢,我去查阅了MySQL的官方文档(https:u002Fu002Fdev.mysql.comu002Fdocu002Frefmanu002F5.6u002Fenu002Ffractional-seconds.html),发现这个特性是在mysql 5.6.4之后开始支持的。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F7527deb7fbec4129bfbc7ff740331b5b” img_width=”1080″ img_height=”387″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003Eu003Cstrongu003E知识点总结u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E经过了前面的实际案例分析和案例复现,想必读者已经对mysql中DATETIME这个类型有了一定的认识,接下来跟我一起看下,我们从这个案例中可以总结出哪些经验。u003Cu002Fpu003Eu003Colu003Eu003Cli class=”ql-align-justify”u003Emysql-connector-java的版本和mysql的版本需要配套使用,例如5.6.4之前的版本,就最好不要使用mysql-connector-java的5.1.23之后的版本,否则就可能会遇到我们这次遇到的问题。u003Cu002Fliu003Eu003Cli class=”ql-align-justify”u003EMySQL中用来表示时间的字段类型有:DATE、DATETIME、TIMESTAMP,它们之间有相同点,各自也有自己的特性,我总结了一个表格,如下所示:u003Cu002Fliu003Eu003Cu002Folu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F0fad38d117dd4eb88bcb9bccbc289246″ img_width=”1080″ img_height=”266″ alt=”一次 Jar 包升级引发的血案 & 解决” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cp class=”ql-align-justify”u003EDATETIME类型在MySQL中是以“YYYYMMDDHHMMSS”格式的整数存放的,与时区无关,使用8个字节的空间;u003Cu002Fpu003Eu003Cp class=”ql-align-justify”u003ETIMESTAMP类型可以保存的时间范围要小很多,显示的值依赖时区,MySQL的服务器、操作系统以及客户端连接都有时区的设置。u003Cu002Fpu003Eu003Cp class=”ql-align-justify”u003E一般情况下推荐使用DATETIME作为时间戳字段,不推荐使用bigint类型来存储时间。u003Cu002Fpu003Eu003Cp class=”ql-align-justify”u003E在开发中,应该尽量避免使用时间戳作为查询条件,如果必须要用,则需要充分考虑MySQL的精度和查询参数的精度等问题。u003Cu002Fpu003Eu003Cu002Fdivu003E”

原文始发于:一次 Jar 包升级引发的血案 & 解决

主题测试文章,只做测试使用。发布者:逗乐男神i,转转请注明出处:http://www.cxybcw.com/26508.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code