【坑】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%';
在這裏插入圖片描述
對比後發現有兩項不一致: 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的版本時,千萬不要覺得直接升級到最新版是明智的選擇,記住:步子大了會扯着蛋。特別是大版本的跨度,不一定完全向下兼容,肯定是會有不小的調整
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章