SpringBoot时间戳与MySql数据库记录相差14小时排错

From: http://www.cnblogs.com/jason1990/archive/2018/11/28/10032181.html

项目中遇到存储的时间戳与真实时间相差14小时的现象,以下为解决步骤.

问题

CREATE TABLE `incident` (`id` int(11) NOT NULL AUTO_INCREMENT,`created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`recovery_time` timestamp NULL DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4;

以上为数据库建表语句,其中created_time是插入记录时自动设置,recovery_time需要手动进行设置.
测试时发现,created_time为正确的北京时间,然而recovery_time则与设置时间相差14小时.

尝试措施

jvm时区设置

//设置jvm默认时间
System.setProperty("user.timezone", "UTC");

数据库时区查询

查看数据库时区设置:

show variables like '%time_zone%';
--- 查询结果如下所示:
--- system_time_zone: CST
--- time_zone:SYSTEM

查询CST发现其指代比较混乱,有四种含义(参考网址:https://juejin.im/post/5902e087da2f60005df05c3d):

  • 美国中部时间 Central Standard Time (USA) UTC-06:00
  • 澳大利亚中部时间 Central Standard Time (Australia) UTC+09:30
  • 中国标准时 China Standard Time UTC+08:00
  • 古巴标准时 Cuba Standard Time UTC-04:00

此处发现如果按照美国中部时间进行推算,相差14小时,与Bug吻合.

验证过程

MyBatis转换

代码中,时间戳使用Instant进行存储,因此跟踪package org.apache.ibatis.type下的InstantTypeHandler.

@UsesJava8
public class InstantTypeHandler extends BaseTypeHandler<Instant> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Instant parameter, JdbcType jdbcType) throws SQLException {ps.setTimestamp(i, Timestamp.from(parameter));}//...代码shenglve
}

调试时发现parameter为正确的UTC时.
函数中调用Timestamp.fromInstant转换为Timestamp实例,检查无误.

    /*** Sets the designated parameter to the given <code>java.sql.Timestamp</code> value.* The driver* converts this to an SQL <code>TIMESTAMP</code> value when it sends it to the* database.** @param parameterIndex the first parameter is 1, the second is 2, ...* @param x the parameter value* @exception SQLException if parameterIndex does not correspond to a parameter* marker in the SQL statement; if a database access error occurs or* this method is called on a closed <code>PreparedStatement</code>     */void setTimestamp(int parameterIndex, java.sql.Timestamp x)throws SQLException;

继续跟踪setTimestamp接口,其具体解释见代码注释.

Sql Driver转换

项目使用com.mysql.cj.jdbc驱动,跟踪其setTimestampClientPreparedStatement类下的具体实现(PreparedStatementWrapper类下实现未进入).

    @Overridepublic void setTimestamp(int parameterIndex, Timestamp x) throws java.sql.SQLException {synchronized (checkClosed().getConnectionMutex()) {((PreparedQuery<?>) this.query).getQueryBindings().setTimestamp(getCoreParameterIndex(parameterIndex), x);}}

继续跟踪上端代码中的getQueryBindings().setTimestamp()实现(com.mysql.cj.ClientPreparedQueryBindings).

    @Overridepublic void setTimestamp(int parameterIndex, Timestamp x, Calendar targetCalendar, int fractionalLength) {if (x == null) {setNull(parameterIndex);} else {x = (Timestamp) x.clone();if (!this.session.getServerSession().getCapabilities().serverSupportsFracSecs()|| !this.sendFractionalSeconds.getValue() && fractionalLength == 0) {x = TimeUtil.truncateFractionalSeconds(x);}if (fractionalLength < 0) {// default to 6 fractional positionsfractionalLength = 6;}x = TimeUtil.adjustTimestampNanosPrecision(x, fractionalLength, !this.session.getServerSession().isServerTruncatesFracSecs());//注意此处时区转换this.tsdf = TimeUtil.getSimpleDateFormat(this.tsdf, "''yyyy-MM-dd HH:mm:ss", targetCalendar,targetCalendar != null ? null : this.session.getServerSession().getDefaultTimeZone());StringBuffer buf = new StringBuffer();buf.append(this.tsdf.format(x));if (this.session.getServerSession().getCapabilities().serverSupportsFracSecs()) {buf.append('.');buf.append(TimeUtil.formatNanos(x.getNanos(), 6));}buf.append('\'');setValue(parameterIndex, buf.toString(), MysqlType.TIMESTAMP);}}

注意此处时区转换,会调用如下语句获取默认时区:

this.session.getServerSession().getDefaultTimeZone()

获取TimeZone数据,具体如下图所示:

TimeZone定义

检查TimeZone类中offset含义,具体如下所示:

    /*** Gets the time zone offset, for current date, modified in case of* daylight savings. This is the offset to add to UTC to get local time.* <p>* This method returns a historically correct offset if an* underlying <code>TimeZone</code> implementation subclass* supports historical Daylight Saving Time schedule and GMT* offset changes.** @param era the era of the given date.* @param year the year in the given date.* @param month the month in the given date.* Month is 0-based. e.g., 0 for January.* @param day the day-in-month of the given date.* @param dayOfWeek the day-of-week of the given date.* @param milliseconds the milliseconds in day in <em>standard</em>* local time.** @return the offset in milliseconds to add to GMT to get local time.** @see Calendar#ZONE_OFFSET* @see Calendar#DST_OFFSET*/public abstract int getOffset(int era, int year, int month, int day,int dayOfWeek, int milliseconds);

offset表示本地时间UTC时的时间间隔(ms).
计算数值offset,发现其表示美国中部时间,即UTC-06:00.

  • Driver推断Session时区为UTC-6;
  • DriverTimestamp转换为UTC-6String;
  • MySql认为Session时区在UTC+8,将String转换为UTC+8.

因此,最终结果相差14小时,bug源头找到.

解决方案

参照https://juejin.im/post/5902e087da2f60005df05c3d.

mysql> set global time_zone = '+08:00';
Query OK, 0 rows affected (0.00 sec)mysql> set time_zone = '+08:00';
Query OK, 0 rows affected (0.00 sec)

告知运维设置时区,重启MySql服务,问题解决.

此外,作为防御措施,可以在jdbc url中设置时区(如此设置可以不用修改MySql配置):

jdbc:mysql://localhost:3306/table_name?useTimezone=true&serverTimezone=GMT%2B8

此时,就告知连接进行时区转换,并且时区为UTC+8.

PS:
如果您觉得我的文章对您有帮助,可以扫码领取下红包,谢谢!

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

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

相关文章

mysql重置root密码方法

2019独角兽企业重金招聘Python工程师标准>>> 1. 先关闭mysqld 2. 运行&#xff1a; mysqld_safe --skip-grant-tables 3. 另开一个窗口&#xff0c;用 mysql -uroot 登录mysql&#xff0c;执行 UPDATE mysql.user SET PasswordPASSWORD(你的密码) WHERE User…

插入排序之C++实现

描述 插入排序是一种简单直观的排序算法。它的基本思想是将一个待排序的数据序列分为已排序和未排序两部分&#xff0c;每次从未排序序列中取出一个元素&#xff0c;然后将它插入到已排序序列的适当位置&#xff0c;直到所有元素都插入完毕&#xff0c;即完成排序。 实现思路…

Spring在Java Filter注入Bean为Null的问题解决

From: https://www.cnblogs.com/EasonJim/p/7666009.html 在Spring的自动注入中普通的POJO类都可以使用Autowired进行自动注入&#xff0c;但是除了两类&#xff1a;Filter和Servlet无法使用自动注入属性。&#xff08;因为这两个归Web容器管理&#xff09;可以用init&#xf…

Mybatis:resultMap的使用总结

From: https://www.cnblogs.com/kenhome/p/7764398.html Mybatis的介绍以及使用&#xff1a;http://www.mybatis.org/mybatis-3/zh/index.html resultMap是Mybatis最强大的元素&#xff0c;它可以将查询到的复杂数据&#xff08;比如查询到几个表中数据&#xff09;映射到一个…

MyBatis总结六:resultMap详解(包含多表查询)

From: https://www.cnblogs.com/Alex-zqzy/p/9296039.html 简介&#xff1a;   MyBatis的每一个查询映射的返回类型都是ResultMap&#xff0c;只是当我们提供的返回类型属性是resultType的时候&#xff0c;MyBatis对自动的给我们把对应的值赋给resultType所指定对象的属性&a…

基于beego一键创建RESTFul应用

2019独角兽企业重金招聘Python工程师标准>>> API应用开发入门 Go是非常适合用来开发API应用的&#xff0c;而且我认为也是Go相对于其他动态语言的最大优势应用。beego在开发API应用方面提供了非常强大和快速的工具&#xff0c;方便用户快速的建立API应用原型&#…

MyBatis中in的使用

From: https://www.cnblogs.com/w-bb/articles/6378031.html foreach的主要用在构建in条件中&#xff0c;它可以在SQL语句中进行迭代一个集合。 foreach元素的属性主要有 item&#xff0c;index&#xff0c;collection&#xff0c;open&#xff0c;separator&#xff0c;close…

MyBatis总结五:#{}和${}的用法和区别

From: https://www.cnblogs.com/blazeZzz/p/9295634.html #{}的用法&#xff1a; 我们发现&#xff0c;在Mapper.xml映射文件中&#xff0c;经常使用#{属性名} 来作为SQL语句的占位符&#xff0c;来映射Sql需要的实际参数 如果只有一个参数 <select id"getUserById&…

iOS多视图代码操作

2019独角兽企业重金招聘Python工程师标准>>> 摘抄 http://supershll.blog.163.com/blog/static/370704362012112021115/ 2.1.1 层次结构 基于树的层次结构对iPhone屏幕上的可见内容进行排序。从主窗口开始&#xff0c;视图以特殊的分层方式布置。所有视图都可以有…

(转)Tiny210v2( S5PV210 ) 平台下 FIMD 对应 的 framebuffer 驱动中,关于 video buffer 的理解...

原文:http://www.arm9home.net/read.php?tid-25938.html 管理提醒&#xff1a; 本帖被 xoom 执行加亮操作(2012-12-13)如之前所说&#xff0c;一直想知道显示数据都在哪个地方&#xff0c;通常的数据&#xff0c;比如 framebuffer 中的显示数据&#xff0c;和OpenGL 处理的数据…

C链表反转(时间复杂度O(n))

面试的时候经常会出现的问题,现在都做一遍,回忆一下,练练手. 这个题目需要注意两点: 1.head->next 要先设置为NULL ,否则反转后,它还是指向之前的next节点 2.需要有一个tmp指针,临时保存p->next的地址,这个在改变一个节点的next地址时,经常会用到 示意图 代码实现 #inclu…

mysql中find_in_set()函数的使用及in()用法详解

From: http://www.manongjc.com/article/2710.html MySQL手册中find_in_set函数的语法解释&#xff1a; FIND_IN_SET(str,strlist) str 要查询的字符串 strlist 字段名 参数以”,”分隔 如 (1,2,6,8,10,22) 查询字段(strlist)中包含(str)的结果&#xff0c;返回结果为null…

浅谈SQLiteOpenHelper之onUpgrade例子

当你看到这个博文&#xff0c;首先你要了解onCreate这个创建方法&#xff0c;再来继续下文!&#xff08;可以参考我的上一个博文http://www.cnblogs.com/896240130Master/p/6119616.html&#xff09; 这个onUpgrade类要在onCreate类的基础上建立&#xff01;我们知道onUpgrade是…

MySQL 字符串分割 SUBSTRING_INDEX函数

From: MySQL 字符串分割 SUBSTRING_INDEX函数 Sql代码 SUBSTRING_INDEX(str,delim,count) 用delim 分割str&#xff0c;取第count个子串 url http://www.medhelp.org/forums/Acne/show/56 Java代码 substring_index(url,"/",1) 结果是http: Java代码 substri…

XEN--转载自鸟哥的linux私房菜

初探 XenXen 的效能為什麼能夠這麼好&#xff1f; Xen 在使用上面有什麼樣的限制&#xff1f;我的一部主機最多可以支援幾個 Xen 的模擬環境&#xff1f; Xen 的模擬環境有幾種類型&#xff1f; 這些疑問我們都得要先知道一下才好&#xff01; 當然&#xff0c;最好能夠有個簡單…

[Mojava 10.14.4] Clover隐藏多余分区, 原来可以这么简单

好不容易把黑苹果装完了, 搞定驱动和引导, 剩下的也没啥了, 但还有一事: 隐藏clover中多余的启动项! 直接上最关键的部分: 1. 打开Clover Configurator, 挂载对应的EFI分区 2. 打开分区,加载对应的config.plist文件 3. 切换到Clover Configurator的Gui界面, 添加需要隐藏的项…

Mybatis高级-resultMap之collection聚集

From: https://aodeng.cc/archives/mybatisgaoji 简介 聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性&#xff0c;属性的javaType&#xff08;一般为ArrayList&#xff09;&#xff1b;列表中对象的类型ofType&#xff08;Java实体类&#xff09;&#…

电脑公司 Ghost XP SP3 国庆特别版 v2011.10

电脑公司 Ghost XP SP3 国庆特别版 v2011.10 文件名称:DNGS_GhostXpSp3_v2011.10.iso文件大小:686.59MB &#xff08;719,945,728字节&#xff09;系统格式:NTFSCRC32:DDEBC170MD5:FA264E6241154EE7F80EFED3CFF1C6A2SHA1:FFEB1C13854AA954D9EE384153815FAF52E0747B电脑城系统 …

TCP中间件_Delphi_client

1、界面 1.1、formMain.pas 1.1.1、 object frmMain: TfrmMainLeft 191Top 103Width 542Height 466Caption frmMainColor clBtnFaceFont.Charset DEFAULT_CHARSETFont.Color clWindowTextFont.Height -11Font.Name MS Sans SerifFont.Style []OldCreateOrder False…

Java、Mysql、MyBatis 中枚举 enum 的使用

From: https://yulaiz.com/java-mysql-enum/ Java 和 MySql 中都有枚举的概念&#xff0c;合理的使用枚举&#xff0c;可以让代码阅读和数据库数据查询更加直观、高效。那么我们怎么使用呢&#xff0c;什么时候使用&#xff0c;两者之间怎么进行数据关联呢&#xff1f;&#x…