升级mysql-connector-java中timestamp的坑

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wangdan199112/article/details/78683732

现象:

connector在5.1.23版本后,java.util.Date的数据中小数秒会被拼接到sql中,导致更新、查询中timestamp用在where条件找不到存在的数据。

问题复盘:

我们项目中之前用到的connector版本是5.1.21,需要升级到5.1.32。昨天升级后发布一台机器发现了报错。报错的方法是完成生成派单(F单insert),然后更新该派单的rootid(F单update)的功能。我们在noah环境上复现了问题,并打开了mybatis输出sql语句的开关,发现输出的派单订单表f_n_order_info中的last_updated字段输出格式为:2017-11-29 16:41:31.088。
FOrderInfo实体类中lastUpdated字段的类型为java.util.Date。

/**
* 最后更新时间
*/
private Date lastUpdated;

mysql中f_n_order_info表中last_updated字段为TimeStamp类型。
last_updated timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP` COMMENT ‘最后更新时间’
出问题的update语句:

  update f_n_order_info
        set last_updated = now(),root_id = id
        where id = #{orderId}
        and last_updated = #{lastUpdated}

查看输出的sql语句日志发现,mybatis输出的sql语句中,无论insert 还是update 中last_updated就是带有小数秒的格式。看似应该没有问题,况且之前21版本是好的,32版本出问题了。所以我们断定是connector版本进行了修改。

问题原因:

查看了connector官方版本修改文档,里面一个5.1.23版本中一个bugfix引起了我们的注意。
The nativeSQL() method would always truncate fractional seconds rather than preserving the
fractional part in the output string. Now Connector/J checks the server version: it preserves the fractional
part for MySQL 5.6.4 and greater, and truncates the fractional part for older versions. (Bug #14748459,
Bug #60598)
参考文档:https://docs.oracle.com/cd/E17952_01/connector-j-relnotes-en/connector-j-relnotes-en.pdf

大意是修改了nativeSQL方法,之前对于时间的做法是截断了小数秒,现在5.6.4版本后保留这个小数秒(也就是说之前connector在拼接sql的时候就直接屏蔽掉了小数秒,在23版本开始这个小数秒要加回来)。insert的时候,我们的字段timestamp没有精度,所以即使我们传入形如2017-11-29 16:41:31.088的数据,mysql只会读取年月日时分秒,而不会保存这个小数秒。正如前面所说21版本之前,connector直接截断了这个小数秒,所以我们的update语句不会出现更新不到的问题。但是23版本以后,connector会带上我们传入的小数秒,那么update语句的where条件中时间就是带有小数秒的,这就相当于用2017-11-29 16:41:31.088去匹配2017-11-29 16:41:31的数据,所以不成功。

问题解决方案:

我们这边初步有两个方案,一个是把update语句中这个过滤条件删除掉,因为这个语句已经有了主键进行更新,不需要这个字段作为辅助条件,而且经过分析,这个业务也不需要乐观锁来避免覆盖,这个方法对于我们修改成本低。第二个就是每次传入Date前,把时间进行格式化,屏蔽掉小数秒,这样查询的时候就不会因为不匹配找不到数据。

反思:

建表的时间字段,个人推荐使用bigint来保存时间戳,这样方便保存也方便查询,也不会被时区等让人头疼的问题烦心。

相关connector5.1.23修改的时间部分代码,可以参考com.mysql.jdbc.EscapeProcessor#processTimeToken这个类中的方法,比对不同版本的差异。

展开阅读全文

没有更多推荐了,返回首页