【坑】emoji表情保存至mysql中报错

报错

java.sql.SQLException: Incorrect string value: '\xF0\x9D\x92\xA9'

背景

  • 保存一个Emoji表情至数据中时出现如标题的报错

原因

  • 当前Mysql数据库字符集设定为:utf8,只能保存3字节的字符
  • emoji表情为4字节字符,保存时报错(一些特殊字符和表达式保存报错一般也是由于这个原因)

解决方案

  • 修改数据库字符集为 utf8mb4

吐槽

  1. 字符集分为数据库级别,表级别,字段级别三个级别;
  2. 由于这个服务是一个很老的核心服务,针对方案是最小最稳妥的改动来修复问题,所以我们采用的是仅仅修改字段级别的字符集为utf8mb4,表级别为utf8保持不动,库级别的下面再说。
  3. 因为在自己电脑上进行DEMO预演过,本来以为很简单,一个SQL的事就解决问题,结果。。。

遭遇坎坷历程

1. 修改字段的字符集为utf8mb4后,插入表情时,报错依旧

怀疑是不是仅修改字段级别字符集无用,数据库级别字符集也需要修改,于是对比了本地数据库和运行环境数据库的字符集

查看数据库级别字符集SQL: show variables like '%char%';
【坑】emoji表情保存至mysql中报错_第1张图片
对比后发现有两项不一致: character_set_database 和 character_set_server。
但苦于运行环境数据库没有权限,没法修改尝试。

2. 具体定位问题方
  • 由于DEMO代码连接本地数据库,插入表情是OK的;问题服务连接运行环境数据库是不OK的;两者代码不同,数据库也不同无法定位问题究竟是由什么引起的
  • 于是将本地DEMO的代码连接运行环境数据库,发现插入表情完全可以,不会报错,定位为服务的代码问题(此时仅仅是修改了数据库字段的字符集)
3. 定位问题点
  • DEMO代码和问题服务均使用durid连接池,但版本不一样,修改相同版本,依旧不行
  • 中间通过万能的百度找解决方案,从druid入手,尝试配置 initConnectionSqls = set names utf8mb4;无用,依旧报错
  • 从数据库连接入手,删掉useUnicode=true&characterEncoding=utf8,依旧无用
  • 从种种百度的解决方案尝试,均无用
  • 时间快到7点了,果断下班回家,陷入死胡同怎么想都是没用的,要放空一下自己
  • 第二天上班,再对比两个代码,发现 mysql-connector-java 使用的版本也是不一致的,demo使用的版本是:8.0.15 问题服务使用的版本是: 5.1.44,修改为相同版本,测试!成了!!! 看来不加班是正确的~

3. 再遇坑,步子大了扯着蛋
  • 服务上线部署到release环境,QA测试时发现,保存数据记录的更新时间不对,检查发现创建时间和更新时间多了8小时
  • 多8小时,东八区;很明显联想到哪边做了时区处理;可是以前遇到的都是少8小时,这次是多8小时;又是一个加班查问题~
  • 写了一个小jar,仅仅打印new Date()获取到的date打印出来,丢到运行环境机器上运行,是东八区时间(证明容器有设置东八区时区)
  • 定位到保存时间代码:java获取new Date(),使用SimpleDateFormat转换为年月日时分秒字符串,保存数据库
  • 数据库保存类型:datetime (小细节1:代码保存的时用的字符串类型,数据库字段却是datetime类型)
  • 操作一个数据,检查数据库保存的时间,发现时间是对的,现在北京时间晚上7点,数据库就是保存的晚上7点,可是调接口获取数据时,从库中取出来的却是第二天的凌晨3点。(定位到:取数据时被加了8小时)
  • 数据库执行:SELECT CURRENT_TIME; 得到时间:中午11点(细节2:数据库是0时区的)
  • 大胆推测,数据库是0时区,保存时间为晚上7点,代码服务设置时区为东八区,获取到时间第二天凌晨3点。嗯,有个操作数据库的东东给我自动处理了时区,真是贴心!!!
  • 想想最近数据库相关的,也就是改字符集的时候改了这个驱动版本,直接从5.1.44升到了8.0.15,真是步子大了扯着蛋,一下跨的版本太大直接升到最新版也不好啊,还是要谨慎啊,最后找到一个解决了字符集,又没有帮忙处理时区的版本:5.1.47
  • 其实不能怪人家数据库驱动做这个贴心的时区处理举动,还是代码问题:如果存值时使用时间类型,人家是会帮忙减8的,这样取的时候加8也是正确的,怪就怪,接手的就已经是这样的代码啊~~~

总结

  1. 想数据库能插入表情符的标准解决方案,不知道~
  2. 数据库级别字符集如上贴图两种没啥影响
  3. druid连接池的 initConnectionSqls 配置每搞明白,不会使用
  4. 数据库连接配置有没有useUnicode=true&characterEncoding=utf8不影响
  5. 看看你的mysql-connector-java包的版本吧
  6. 升级较新的版本后有个警告,是让你把driver-class-name由com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver
  7. 当需要升级某个jar的版本时,千万不要觉得直接升级到最新版是明智的选择,记住:步子大了会扯着蛋。特别是大版本的跨度,不一定完全向下兼容,肯定是会有不小的调整

你可能感兴趣的:(坑,Mysql)