springboot项目与数据库交互时中文编码错误问题(非???乱码)

  1. 背景
    先概述一下我的项目编码,项目使用的rpc框架让前后端分离,前端传递浏览器的请求然后通过socket传递数据,远程调用后端的服务。前端设置了tomcat使用utf-8编码,后端数据源使用utf8;mysql的client、mysqld、mysql system、数据表都是使用utf8mb4(mysql中的utf8)。你可以先确定自己项目中以上方面是否都使用了正确的编码,再往下继续看。

  2. 问题
    当浏览器传递中文相关的查询请求时,在windows运行的前端时显示还是正常的(utf8形式输出);前端通过socket传递给在windows运行的后端时显示也是正常的(utf8输出);但是当后端发查询语句给在linux运行的mysql时,查阅日志发现变成了生僻字,与原来不同(utf输出,但还是中文),导致查询没有结果,如图:
    (浏览器输入的莎士比亚(utf8)在mysql日志中显示(utf8),图中单引号中文字):
    springboot项目与数据库交互时中文编码错误问题(非???乱码)
    更奇怪的是,如果后端程序通过gui下的idea运行的话,表现正常;但是如果在powershell下用
    mvn spring-boot:run
    或是
    java -jar xx.jar
    运行后端程序的话就会像上面说的那样表现异常。

  3. 问题排查

我试着把后端放在linux上,然后通过bash运行,发现表现正常。
于是浏览器->前端是正常的;前端->后端通过打印到控制台是正常的(如果打印到powershell,要记得powershell默认是gbk,需先改代码页为65001);然后到了mysql就变怪了,所以问题出在后端->数据库。事实证明后端移到linux上也解决了问题。

想起来java的默认编码好像与平台相关,于是试着在powershell用

System.out.println(Charset.defaultCharset());

打印默认编码,果然是gbk!然而在idea控制台打印是utf-8!是windows的锅。

  1. 问题原因

(下面将图中那串生僻字用生僻字代替)
所以错码原因就是,前端将字符串“莎士比亚”的utf-8编码(二进制形式记为x)通过socket发给后端,后端以为这是gbk编码,所以utf8的"莎士比亚"代表的二进制串x被翻译成了gbk的 生僻字(二进制表示也是x); 因为约定了与数据源mysql通信使用utf8,所以后端将gbk的生僻字(二进制为x)翻译成utf8的生僻字(二进制为y,然而他们显示出来都是生僻字)。

  1. 解决办法

所以解决办法就是去更改jvm运行时编码。网上找了许多怎么更改defaultCharset的方法,jvm启动参数、环境变量什么的方法,皆无效。遂在后端收取前端数据时的输入输出流指定编码为utf8:

try (BufferedReader clientMessage = new BufferedReader(new InputStreamReader(
                    incomingSocket.getInputStream(), StandardCharsets.UTF_8)); PrintStream serverMessage = new PrintStream(
                    incomingSocket.getOutputStream(), true, StandardCharsets.UTF_8)) 

问题就得以解决了。

上一篇:MySQL数据乱码问题的解决


下一篇:mysql中文乱码问题