mysql-connector-java 插入 utf8mb4 字符失败问题处理分析

当MySQL数据库编码从utf8改为utf8mb4后,使用mysql-connector-java遇到插入表情等四字节字符失败的问题。问题源于数据库返回的编码未更新。解决方法包括重启MySQL服务、打补丁或升级Connector/J版本。
摘要由CSDN通过智能技术生成

问题说明

业务数据库实例的编码由 utf8 修改为 utf8mb4 后, java 业务插入表情符等宽字符(4 字节)的时候一直报错以下相关的错误:

### Cause:java.sql.SQLException:Incorrect string value:\xF0\x9F\x98\x8E for column nick_name at row 1
;uncategorized SQLException for SQL[]; SQL state [HY000]; error code[1366];Incorrect string value: \xF0\x9F\x98\x8E for column nick_name at row 1

程序及数据库运行的版本及环境如下所示:

Centos 7.6
kernel-3.10.0-957.1.3.el7.x86_64
mysql-connector-java-5.1.46
Percona-Server-5.6.38-rel83.0-Linux

测试环境中使用同样的 mysql-connector-java 版本, 程序可以正常插入. 所不同的是测试环境修改完编码后重启了 MySQL 服务, 线上环境仅做以下修改, 重启程序而不重启 MySQL 服务:

set global character_set_client = utf8mb4;
set global character_set_connection = utf8mb4;
set global character_set_database = utf8mb4;
set global character_set_results = utf8mb4;
set global character_set_server = utf8mb4;
set global collation_server = utf8mb4_general_ci;
set global collation_database = utf8mb4_general_ci;
set global collation_connection = utf8mb4_general_ci;

jdbc 配置说明

参考 connector-j-reference-charset 可以看到如果程序要插入 utf8mb4 字符, 需要满足以下条件:

Connector/J 5.1.47 及以上版本:
  1. 指定 characterEncoding 参数为 UTF8/UTF-8 即可, 新版本直接映射到 utf8mb4 编码;
  2. 如果 connectionCollation 指定的排序规则不是 utf8mb4 相关的, 则 characterEncoding 参数会重写为排序规则对应的编码;

Connector/J 5.1.47 以下版本:
  1. 设置 MySQL 参数变量 character_set_server=utf8mb4;
  2. 指定 characterEncoding 参数为 UTF8/UTF-8, jdbc 程序会进行探测是否使用 utf8mb4;

所以对于 mysql-connector-java 版本来讲, 我们的条件已经满足, 不过还是插入失败. 另外 characterEncoding 参数的值只可以指定 connector-j-reference-charset 链接中 Table 5.3提到的编码名, 指定其余的编码名, jdbc 在建立连接的时候就是失败报错.

问题分析说明

mysql-connect-java 如何处理编码

满足了官方文档中的条件还是插入失败, 而使用 python, perl 等脚本程序却可以正常插入 utf8mb4 字符, 这点很让人迷惑. 我们参考 mysql-connector-java-5.1.46 的源程序可以看到以下代码:

//src/com/mysql/jdbc/ConnectionImpl.java
1616    private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException {
1617        String realJavaEncoding = getEncoding();
......
1689                     if (realJavaEncoding != null) {
1690 
1691                         //
1692                         // Now, inform the server what character set we will be using from now-on...
1693                         //
1694                         if (realJavaEncoding.equalsIgnoreCase("UTF-8") || realJavaEncoding.equalsIgnoreCase("UTF8")) {
1695                             // charset names are case-sensitive
1696 
1697                             boolean utf8mb4Supported = versionMeetsMinimum(5, 5, 2);
1698                             boolean useutf8mb4 = utf8mb4Supported && (CharsetMapping.UTF8MB4_INDEXES.contains(this.io.serverCharsetIndex));
1699 
1700   
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值