下面我将从根本原因、排查步骤、解决方案(从最推荐到备选)三个方面,为你详细讲解如何解决这个问题。

根本原因:字符集不一致
想象一下你的中文数据("你好")要经过以下旅程才能存入数据库:
- Java 代码:字符串 "你好" 在内存中,它有一个默认的编码(通常是 JVM 启动时操作系统的编码,
UTF-8)。 - JDBC 驱动:JDBC 驱动将这个字符串转换成一种能在网络上传输的格式(字节流)。
- MySQL 服务器:服务器接收这些字节流,并根据它自己的配置来“解释”这些字节,将其转换成数据库内部使用的字符。
- MySQL 数据库/表/列:数据被存储到指定数据库、表、列中,这些层级都有各自的字符集设置。
乱码发生的本质就是:数据在某个环节被用 A 编码写入了,但在另一个环节却被用 B 编码读取了。 数据被用 GBK 编码写入了,但 MySQL 却用 UTF-8 去解读,自然就会得到一堆看不懂的乱码。
要解决这个问题,必须确保从 Java 代码到 MySQL 数据库的每一个环节都使用统一的字符集,强烈推荐 UTF-8。
排查步骤(如何找到问题点)
在动手修改之前,先按照以下步骤排查,确定问题出在哪一步。
检查 MySQL 数据库、表、列的字符集
这是最常见的问题点,登录到你的 MySQL 服务器,执行以下命令:
-- 1. 检查 MySQL 服务器默认的字符集 SHOW VARIABLES LIKE 'character_set_server'; -- 2. 检查当前数据库的字符集 USE your_database_name; SHOW VARIABLES LIKE 'character_set_database'; -- 3. 检查表的字符集 SHOW CREATE TABLE your_table_name; -- 4. 检查列的字符集 (你的 name 列) SHOW FULL COLUMNS FROM your_table_name LIKE 'name';
关键点:
character_set_server和character_set_database最好是utf8mb4。SHOW CREATE TABLE的结果中,表定义里应该有DEFAULT CHARSET=utf8mb4。- 列的
Collation(校对规则)应该以utf8mb4开头,utf8mb4_general_ci或utf8mb4_unicode_ci。
如果这些显示的不是 utf8mb4,那么很可能是问题所在。
检查 JDBC 连接 URL
这是第二个最常见的问题点,确保你的 JDBC URL 中明确指定了字符集。
错误示例:
jdbc:mysql://localhost:3306/your_database
正确示例(推荐):
jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=UTF-8
重要提示: 从 JDBC 驱动 1.20 版本开始,characterEncoding 参数在某些情况下可能被忽略,更推荐使用 serverTimezone 参数,它通常会间接处理字符集问题,并且能避免时区问题。
更现代、更推荐的 URL 写法:
jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
检查 Java 源文件编码
确保你的 Java 源文件(.java 文件)本身是以 UTF-8 编码保存的。
- IDE (如 IntelliJ IDEA / Eclipse):检查 IDE 的文件编码设置,确保是
UTF-8,在 IDEA 中,可以通过File -> Settings -> Editor -> File Encodings查看。 - 命令行编译:如果使用
javac命令编译,确保指定了编码:javac -encoding UTF-8 YourJavaFile.java。
检查项目构建工具(Maven/Gradle)
如果你的项目使用 Maven 或 Gradle,确保编译和运行时都使用 UTF-8。
Maven (pom.xml):
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
Gradle (build.gradle):
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
解决方案(从最推荐到备选)
统一使用 utf8mb4(强烈推荐)
utf8mb4 是 utf8 的超集,它完全兼容 utf8,并且能够存储更多的字符,包括 Emoji 表情和一些罕见的汉字。这是目前业界公认的最佳实践。
修改 MySQL 数据库
如果你是新项目,在建库时直接指定字符集:
CREATE DATABASE your_database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
如果你已有数据库,修改其字符集:
ALTER DATABASE your_database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
修改 MySQL 表
在建表时指定字符集:
CREATE TABLE your_table_name (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
对于已存在的表,修改字符集:
ALTER TABLE your_table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
注意:
CONVERT TO会尝试将现有数据从旧编码转换到新编码,如果数据已经是乱码,这个转换也无法恢复,所以最好的时机是在数据出现乱码之前进行。
修改 JDBC URL
使用推荐的 URL 格式:
String url = "jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
检查 Java 源码和构建工具
按照第二部分的“排查步骤”确保 Java 源码、IDE、Maven/Gradle 都配置为 UTF-8。
统一使用 gbk(不推荐,仅作为备选)
如果你的数据库、应用环境、操作系统等所有环节都强制要求使用 GBK 编码,那么你需要将所有环节都统一为 GBK,但这会增加维护成本,并且无法支持 UTF-8 的字符集,通常不推荐。
修改 JDBC URL:
// 注意:这里的字符集是 GBK String url = "jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=GBK&serverTimezone=Asia/Shanghai";
修改 MySQL:
数据库、表、列的字符集都需要设置为 gbk。
在 Java 代码中进行硬编码转换(不推荐,最后手段)
这种方法治标不治本,只在无法修改数据库配置或 URL 的极端情况下使用,它非常脆弱,容易出错。
原理:在将字符串传给 JDBC 之前,手动将其编码成 GBK 的字节数组,再让 JDBC 驱动按 GBK 解读。
import java.nio.charset.StandardCharsets;
String originalName = "你好";
// 假设数据库是 GBK 编码
byte[] gbkBytes = originalName.getBytes("GBK"); // 手动编码成 GBK
String gbkName = new String(gbkBytes, "GBK"); // 创建一个 GBK 编码的字符串对象
// 然后将 gbkName 传给 PreparedStatement
// pstmt.setString(1, gbkName);
缺点:代码臃肿,可读性差,如果未来数据库字符集变了,所有这些代码都要改,非常容易出错。
总结与最佳实践
要彻底解决 Java 插入 MySQL 中文乱码问题,请遵循以下黄金法则:
- 首选字符集:在整个技术栈中全面拥抱
utf8mb4。 - 数据库层:创建数据库和表时,显式指定
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。 - 连接层:JDBC URL 必须包含
?useUnicode=true&characterEncoding=UTF-8,并加上serverTimezone。 - 应用层:确保 Java 源文件、IDE、Maven/Gradle 的编码都是
UTF-8。 - 驱动版本:尽量使用较新版本的 MySQL Connector/J 驱动。
只要确保了以上五点,你的中文数据就能在整个“数据链路”中畅通无阻,不再出现乱码问题。
