MySQL 04-EMOJI 表情與 UTF8MB4 的故事

拓展閱讀

MySQL View

MySQL truncate table 與 delete 清空表的區別和坑

MySQL Ruler mysql 日常開發規範

MySQL datetime timestamp 以及如何自動更新,如何實現範圍查詢

MySQL 06 mysql 如何實現類似 oracle 的 merge into

MySQL 05 MySQL入門教程(MySQL tutorial book)

MySQL 04- EMOJI 表情與 UTF8MB4 的故事

MySQL Expression 1 of ORDER BY clause is not in SELECT list,references column

emoji

想在 mysql 數據庫插入 emoji 表情,結果報錯:

### Cause: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x92\x8B' for column 'name' at row 1

錯誤原因很多小夥伴也知道,mysql 種的 utf8 和 java 的 utf-8 並不是完全對等的。

應該指定 mysql 的編碼爲 utf8mb4 纔是正確的。

修改編碼

查看編碼

show variables like 'character_set_database'; # 查看數據庫編碼
show create table comment; # 查看錶編碼

修改數據庫 & 表編碼

可以在原來的基礎上直接修改:

alter database <數據庫名> character set utf8mb4; # 修改數據庫
alter table <表名> character set utf8mb4; # 修改表
alter table <表名> change <字段名> <字段名> <類型> character set utf8mb4; # 修改字段

建表時指定

drop database echo_blog;
CREATE DATABASE echo_blog DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

drop table comment;
create table comment
(
    id int unsigned auto_increment comment '主鍵' primary key,
    create_time timestamp default CURRENT_TIMESTAMP not null comment '創建時間',
    update_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新時間'
) comment '評論信息' ENGINE=Innodb default charset=UTF8MB4 auto_increment=1;

亂碼問題

修改完成之後,插入成功。

但是數據庫中全部是 ??? 之類的內容,而不是表情內容。

到底哪裏出問題了呢?

mysql 編碼問題

查看 mysql 編碼

SHOW VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%';

如下:

mysql> SHOW VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%';
+--------------------------+----------------------------------------------------+
| Variable_name            | Value                                              |
+--------------------------+----------------------------------------------------+
| character_set_client     | utf8mb4                                            |
| character_set_connection | utf8mb4                                            |
| character_set_database   | utf8mb4                                            |
| character_set_filesystem | binary                                             |
| character_set_results    | utf8mb4                                            |
| character_set_server     | utf8mb4                                            |
| character_set_system     | utf8                                               |
| character_sets_dir       | D:\tools\mysql\mysql-5.7.24-winx64\share\charsets\ |
| collation_connection     | utf8mb4_unicode_ci                                 |
| collation_database       | utf8mb4_unicode_ci                                 |
| collation_server         | utf8mb4_unicode_ci                                 |
+--------------------------+----------------------------------------------------+

屬性說明:

character_set_client

主要用來設置客戶端使用的字符集。通俗的講就是mysql把客戶端傳遞過來的數據都當成是utf8mb4

character_set_connection

主要用來設置連接數據庫時的字符集,如果程序中沒有指明連接數據庫使用的字符集類型則按照這個字符集設置。

character_set_database

主要用來設置默認創建數據庫的編碼格式,如果在創建數據庫時沒有設置編碼格式,就按照這個格式設置。

character_set_filesystem

文件系統的編碼格式,把操作系統上的文件名轉化成此字符集,即把 character_set_client轉換character_set_filesystem, 默認binary是不做任何轉換的。

character_set_results

數據庫給客戶端返回時使用的編碼格式,如果沒有指明,使用服務器默認的編碼格式。通俗的講就是mysql發送個客戶端的數據是utf8mb4的

character_set_server

服務器安裝時指定的默認編碼格式,這個變量建議由系統自己管理,不要人爲定義。

character_set_system

數據庫系統使用的編碼格式,這個值一直是utf8,不需要設置,它是爲存儲系統元數據的編碼格式。

character_sets_dir

這個變量是字符集安裝的目錄。

Mysql的字符集內部處理

1.mysql Server收到請求時將請求數據從 character_set_client 轉換爲 character_set_connection

2.進行內部操作前將請求數據從 character_set_connection 轉換爲內部操作字符集,步驟如下

  A. 使用每個數據字段的 CHARACTER SET 設定值;

  B. 若上述值不存在,則使用對應數據表的字符集設定值

  C. 若上述值不存在,則使用對應數據庫的字符集設定值;

  D. 若上述值不存在,則使用 character_set_server 設定值。

3.最後將操作結果從內部操作字符集轉換爲 character_set_results

臨時修改配置

上面的配置都可以通過命令臨時修改:

SET character_set_client = utf8mb4;
SET character_set_connection = utf8mb4;
SET character_set_database = utf8mb4;
SET character_set_results = utf8mb4;
SET character_set_server = utf8mb4;

SET collation_connection = utf8mb4_unicode_ci;
SET collation_database = utf8mb4_unicode_ci;
SET collation_server = utf8mb4_unicode_ci;

當然,也可以通過修改 my.ini 配置文件。

修改 mysql 服務器配置文件

比如 windows 下個人的 mysql 安裝目錄爲:D:\tools\mysql\mysql-5.7.24-winx64

那就在下面創建 my.ini(如果沒有的話)。

內容如下:

[mysql]
# 設置mysql客戶端默認字符集
default-character-set=utf8mb4
 
[mysqld]
# 設置3306端口
port=3306
# 允許最大連接數
max_connections=20
# 服務端使用的字符集默認爲8比特編碼的latin1字符集
character-set-server=utf8mb4
# 創建新表時將使用的默認存儲引擎
default-storage-engine=INNODB
 
collation-server=utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'

character-set-client-handshake = FALSE
explicit_defaults_for_timestamp=true
 
[client]
default-character-set=utf8mb4

修改完成後需要重啓 mysql 服務。

可以在 bin 下執行 mysqld restart。這個實踐下來只初始化了部分編碼。

個人實在 windows services(服務) 下,把 mysql 服務進行了重新啓動。

jdbc 配置

druid 數據源配置

spring:
  datasource:
    druid:
      username: root
      password: xxxxxx
      url: jdbc:mysql://localhost:3306/echo_blog?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
      driver-class-name: com.mysql.jdbc.Driver
      connection-init-sqls: set names utf8mb4;

官方資料

https://dev.mysql.com/doc/connectors/en/connector-j-reference-charsets.html

Notes
For Connector/J 8.0.12 and earlier: In order to use the utf8mb4 character set for the connection, the server MUST be configured with character_set_server=utf8mb4; if that is not the case, when UTF-8 is used for characterEncoding in the connection string, it will map to the MySQL character set name utf8, which is an alias for utf8mb3.

For Connector/J 8.0.13 and later:

When UTF-8 is used for characterEncoding in the connection string, it maps to the MySQL character set name utf8mb4.

If the connection option connectionCollation is also set alongside characterEncoding and is incompatible with it, characterEncoding will be overridden with the encoding corresponding to connectionCollation.

Because there is no Java-style character set name for utfmb3 that you can use with the connection option charaterEncoding, the only way to use utf8mb3 as your connection character set is to use a utf8mb3 collation (for example, utf8_general_ci) for the connection option connectionCollation, which forces a utf8mb3 character set to be used, as explained in the last bullet.

Warning
Do not issue the query SET NAMES with Connector/J, as the driver will not detect that the character set has been changed by the query, and will continue to use the character set configured when the connection was first set up.

說明:

提示
mysql-connector-java 版本在8.0.12之前的,包括8.0.12,服務端必須設置character_set_server=utf8mb4;如果不是的話,就算設置了characterEncoding=UTF-8,照樣會被設置爲MYSQL的 utf8字符集,也就是utf8mb3。

對於8.0.13和以後的版本,如果設置了characterEncoding=UTF-8,他會映射到MYSQL的utf8mb4字符集。

如果connectionCollation 也和characterEncoding一起設置了,但是不兼容,characterEncoding會被connectionCollation的設置覆蓋掉。

由於沒有Java-Style的utfmb3對應的字符集名稱可以用在connection選項charaterEncoding上,唯一的設置utf8mb3的方式就是在連接選項設置utf8mb3 collation(例如utf8_general_ci),這會強制使用utf8mb3字符集,正如上文所述。

警告
不要通過Connector發起SET NAMES指令,因爲driver不會檢測字符集是不是被查詢語句改動,並且當連接第一次建立之後,會繼續使用當時的字符集設置。

可以發現 jdbc 中的配置 connection-init-sqls: set names utf8mb4; 這句話是沒啥用的。

建議老老實實的修改 mysql 服務端的配置。

依然亂碼

這個時候 java 客戶端保存 emoji,依然有部分亂碼。

比如:

💔✊💓💖😧😯 I Love this!

用命令行查看,數據庫變成了:

 �✊���� I Love this!

部分亂碼? what's up?

java 程序斷點

在 java 應用中進行斷點,發現內容是對的。

mysql 命令行插入

我們直接在 mysql 命令行執行插入:

insert into comment (content) values ('💔✊💓💖😧😯 I Love this!');

MD,發現無法執行,這條路走不通。

命令行終端不支持 emoji 表情。

亂碼的原因

這個亂碼是因爲 mysql 終端導致的,還是別的原因?

我們測試一下,使用 java 程序對內容進行查詢。

測試代碼

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class CommentServiceTest {

    @Autowired
    private CommentService commentService;

    @Test
    public void selectTest() {
        Comment comment = commentService.selectById(20);
        System.out.println(comment);
    }

}

測試效果

日誌如下:

Comment{id=20, content=💔✊💓💖😧😯 I Love this!, ackFlag=N, createTime=Sun Aug 15 10:13:56 CST 2021, updateTime=Sun Aug 15 10:13:56 CST 2021}

可以發現,存儲的數據本身是沒有問題的。

是命令行終端的問題。

命令行的問題

已有的命令行

一開始使用的是 windows10 自帶的 cmd,發現不行。

使用 cmder 命令行,還是不行。

powershell

我們測試下微軟商店的 Terminal 命令行,也就是新版本的 powershell。

cd D:\tools\mysql\mysql-5.7.24-winx64\bin
.\mysql -uroot -p

登錄後執行查詢,結果如下:

💔✊💓💖😧😯 I Love this! 

糾結了半天的亂碼,經確認是命令行終端的問題。

參考資料

MySQL的utf8、utf8mb4、編碼問題詳解

mysql字符集utf8mb4失效踩坑

Mysql UTF-8mb4字符集的問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章