乱码的根本原因
乱码的本质是 “编码和解码所使用的字符集不一致”。

想象一下:
- 发送方 (Java):把一串中文字符(如 "你好")按照 编码A(如
GBK)转换成一串二进制数据(E4 BD A0 E5 A5 BD)。 - 接收方 (Oracle):接收到这串二进制数据后,却按照 编码B(如
ISO-8859-1)去解析,试图还原成文字,由于E4 BD A0在ISO-8859-1中找不到对应的字符,于是就显示成了乱码(如 )。
我们的目标就是确保从 Java 应用到 Oracle 数据库的整个链路中,所有环节都使用 同一种字符集。
完整排查与解决方案(从易到难)
请按照以下步骤逐一排查,通常问题都能解决。
步骤 1:检查数据库服务端的字符集
这是最根本的设置,数据库的字符集决定了它能正确存储和显示哪些语言。

-
登录到 Oracle 数据库:
sqlplus / as sysdba
-
查询数据库字符集:
SELECT value$ FROM sys.props$ WHERE name = 'NLS_CHARACTERSET';
- 推荐字符集:
AL32UTF8,这是 Oracle 推荐的字符集,完全兼容 Unicode,可以存储世界上几乎所有语言的字符,是解决国际化问题的首选。 - 常见问题字符集:
ZHS16GBK或WE8ISO8859P1。ZHS16GBK只支持中文和少数亚洲语言,而WE8ISO8859P1基本不支持中文,非常容易出乱码。
- 推荐字符集:
如果数据库字符集不是 AL32UTF8,并且业务有国际化需求,强烈建议在合适的时候进行字符集迁移(这是一个复杂的操作,需要谨慎规划)。
步骤 2:检查 JDBC 连接 URL 的字符集设置
这是最直接、最常用的解决方案,在 JDBC 连接字符串中显式指定客户端和服务器之间的字符集。

问题 URL:
String url = "jdbc:oracle:thin:@localhost:1521:ORCL";
修正后的 URL (推荐使用 AL32UTF8):
// 显式指定使用 AL32UTF8 字符集进行通信 String url = "jdbc:oracle:thin:@localhost:1521:ORCL?useUnicode=true&characterEncoding=UTF-8"; // 如果数据库是 ZHS16GBK,则指定为 GBK // String url = "jdbc:oracle:thin:@localhost:1521:ORCL?useUnicode=true&characterEncoding=GBK";
参数解释:
useUnicode=true: 告诉 JDBC 驱动使用 Unicode 字符流。characterEncoding=UTF-8(或GBK): 指定 Java 应用程序与数据库之间网络传输时使用的字符编码。这个编码最好和数据库服务端的字符集保持一致。
最佳实践:将数据库字符集设置为 AL32UTF8,JDBC URL 也使用 characterEncoding=UTF-8,这样整个系统都基于 Unicode,兼容性最好。
步骤 3:检查 Java 源代码文件的编码
确保你的 Java 源代码文件(.java 文件)本身是以 UTF-8 编码保存的。
- IDE (如 IntelliJ IDEA, Eclipse) 设置:
- 在 IDE 的设置中,将项目文件的默认编码设置为
UTF-8。 - 确保编译时使用的也是
UTF-8编码,对于 Maven 项目,可以在pom.xml中指定:<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties>
- 在 IDE 的设置中,将项目文件的默认编码设置为
- 编译命令行:
- 使用
-encoding UTF-8参数进行编译:javac -encoding UTF-8 YourJavaFile.java
- 使用
如果源文件编码不对,比如用 GBK 保存了代码,但编译器默认按 UTF-8 解析,那么代码里的中文注释或字符串本身就可能变成乱码。
步骤 4:检查 Web 应用的响应编码
如果你的 Java 应用是一个 Web 项目(如 Spring Boot, Servlet),除了从数据库读,还要确保输出到浏览器时编码正确。
-
设置 Response 编码: 在 Servlet 的
doGet或doPost方法中,或者在框架的过滤器中,设置响应的字符编码。// 方式一:Servlet API response.setContentType("text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); // 方式二:Spring Boot (推荐在配置文件或配置类中全局设置) # application.properties server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true server.servlet.encoding.force=true -
检查 HTML/JSP 页面:
- HTML
<meta>:在 HTML 文件的<head>部分添加:<meta charset="UTF-8">
- JSP 页面:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
- HTML
步骤 5:检查 Oracle 客户端工具的编码
如果你使用 SQL*Plus, PL/SQL Developer, DBeaver 等工具连接数据库并查询数据,这些工具本身也有字符集设置。
- *SQLPlus**:
在 Windows 上,SQL*Plus 的窗口字体可能不支持 UTF-8,可以尝试修改其属性中的字体,或者使用更现代的客户端工具。
- PL/SQL Developer:
Tools -> Preferences -> Connection -> Oracle Client:确保你的 Oracle 客户端路径正确。Tools -> Preferences -> Connection -> SQL Window:可以尝试设置NLS_LANG环境变量,SIMPLIFIED CHINESE_CHINA.AL32UTF8。
- DBeaver:
Database -> Driver Manager -> [你的 Oracle 驱动] -> Edit:在Advanced Settings中,可以设置Session Init SQL为ALTER SESSION SET NLS_LANGUAGE='SIMPLIFIED CHINESE'; ALTER SESSION SET NLS_TERRITORY='CHINA';。
最佳实践方案
为了从根本上杜绝中文乱码问题,推荐采用以下 “三统一” 方案:
-
统一数据库字符集:
- 将 Oracle 数据库的
NLS_CHARACTERSET设置为AL32UTF8,这是最核心的一步。
- 将 Oracle 数据库的
-
统一 JDBC 连接编码:
- 在 JDBC URL 中明确指定字符集:
jdbc:oracle:thin:@host:port:sid?useUnicode=true&characterEncoding=UTF-8
- 在 JDBC URL 中明确指定字符集:
-
统一应用编码:
- 源代码:所有
.java文件保存为UTF-8编码。 - Web 项目:在响应头和页面中统一使用
UTF-8。 - 项目构建:Maven/Gradle 项目配置文件编码为
UTF-8。
- 源代码:所有
特殊情况:已存乱码数据的修复
如果你的数据库中已经存在乱码数据,修复起来会比较麻烦,因为乱码数据是“不可逆”的,你无法知道 原本是什么字。
修复思路:你需要知道当初是 “用 A 编码存储,却用 B 编码读取”,然后反过来,用 “B 编码写入,用 A 编码读取” 来“纠正”它。
示例场景:
- 数据库字符集是
WE8ISO8859P1。 - JDBC URL 没有指定编码,导致 Java 用系统默认编码(如
GBK)发送中文,数据库用ISO-8859-1存储,导致乱码。 - 数据库里存的乱码是 。
修复步骤:
- 创建一个临时表,字符集和原表一致。
- 使用正确的编码进行“纠正”插入:
-- 假设乱码列是 name VARCHAR2(100) -- 原始编码是GBK,存储成了ISO-8859-1 -- 现在我们用ISO-8859-1去解释这些乱码字节,再转换成GBK插入 INSERT INTO temp_table (id, corrected_name) SELECT id, UTL_I18N.STRING_TO_CHAR( UTL_RAW.CAST_TO_VARCHAR2(UTL_I18N.CHAR_TO_RAW(name, 'WE8ISO8859P1')), 'ZHS16GBK' ) FROM original_table;这个 SQL 语句非常复杂且容易出错,请务必在测试环境充分验证。
更推荐的做法:对于已经乱码的数据,如果业务允许,最好的方式是重新从数据源获取正确的数据,然后清空表重新导入,如果无法重新获取,那么就需要根据上述原理进行非常谨慎的修复操作。
希望这份详细的指南能帮助你解决 Java Oracle 中文乱码问题!
