MySQL 預設 latin1 (ISO-8859-1),Java 內部用 UTF-16,若連接層未正確聲明字符集,插入時「UTF-16 → latin1」截斷多字節字符,查詢時「latin1 → UTF-16」產生錯誤映射,形成亂碼。
Java String (UTF-16)
↓ JDBC Driver 轉換 (connectionEncoding)
網絡傳輸 (character_set_client)
↓ MySQL Server 轉換
存儲 (character_set_column / table / database)
↓ 讀取時反向轉換
結果集 (character_set_results)
↓ JDBC Driver 轉換
Java String (UTF-16)
jdbc:mysql://localhost:3306/dbname?
useUnicode=true&
characterEncoding=utf8mb4&
serverTimezone=Asia/Shanghai&
useSSL=false&
allowPublicKeyRetrieval=true
useUnicode=true:啟用字符集轉換characterEncoding=utf8mb4:指定傳輸編碼 (需 MySQL 5.5.3+)serverTimezone:避免時區警告/etc/my.cnf (治本)[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4' -- 非超級用戶自動生效
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
重啟後,新建表/庫自動繼承 utf8mb4,存量表需 ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4;
String str = "中文";
// 關鍵:先獲取 UTF-8 字節,再按 ISO-8859-1 重構字符串
// 相當於告訴驅動:「這字符串已是 latin1 編碼」
str = new String(str.getBytes("UTF-8"), "ISO-8859-1");
String sql = "INSERT INTO tb (col) VALUES (?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, str);
ps.executeUpdate();
String str = rs.getString(1);
// 關鍵:按 ISO-8859-1 取字節,再按 UTF-8 解碼還原
str = new String(str.getBytes("ISO-8859-1"), "UTF-8");
// 或指定目標編碼 (如 GBK)
str = new String(str.getBytes("ISO-8859-1"), "GBK");
原理: 驅動按 characterEncoding 解碼網絡字節。若連接參數未生效 (如舊版驅動、連接池缓存),手動構造「偽 latin1 字符串」欺騙驅動按 latin1 發送,MySQL 再按列字符集 (utf8mb4) 正確存儲。
| 檢查點 | 命令/代碼 | 期望值 |
|---|---|---|
| 資料庫字符集 | SHOW VARIABLES LIKE 'character_set_%'; | database/server/connection/results/client 全為 utf8mb4 |
| 表字符集 | SHOW CREATE TABLE t; | CHARSET=utf8mb4 |
| 列字符集 | SHOW FULL COLUMNS FROM t; | Collation: utf8mb4_unicode_ci |
| 連接字符集 | SHOW VARIABLES LIKE 'character_set_connection'; | utf8mb4 |
| JDBC 實際編碼 | conn.getMetaData().getDriverVersion() + 抓包分析 | 發送字節為 UTF-8 編碼 |
utf8 只支援 3 字節 (BMP),Emoji/生僻字需 utf8mb4 (4 字節)caching_sha2_password 需驅動 8.0.13+ 或建立用戶指定 mysql_native_passwordmysql、information_schema 等系統庫不影響業務數據character-set-server=utf8mb4 + 連接 URL characterEncoding=utf8mb4 雙重保險SET NAMES utf8 (SQL 注入風險、連接池失效)