mysql 4字节utf8_MySQL 4字节utf8字符更新失败一例

MySQL 4字节utf8字符更新失败一例

业务的小伙伴反映了下面的问题

问题

有一个4字节的utf8字符'????'插入到MySQL数据库中时报错

java.sql.SQLException: Incorrect string value: '\xF0\xA0\x99\xB6' for column 'c_utf8mb4' at row 1

数据库中存放该字符的列已经定义为utf8mb4编码了,但相关的参数character_set_server的值为utf8。 比较奇怪的是使用mysql-connector-java-5.1.15.jar驱动时没有问题,使用更高版本的驱动如mysql-connector-java-5.1.22.jar,就会出错。JDBC的下面2个连接参数,不过设置与否,都没有影响。

characterEncoding=utf8

useUnicode=true

原因

jdbc驱动未正确设置SET NAMES utf8mb4导致转码错误。

根据MySQL官方手册,在MySQL Jdbc中正确使用4字节UTF8字符的方法如下:

http://dev.mysql.com/doc/relnotes/connector-j/5.1/en/news-5-1-14.html:

Connector/J mapped both 3-byte and 4-byte UTF8 encodings to the same Java UTF8 encoding.

To use 3-byte UTF8 with Connector/J set characterEncoding=utf8 and set useUnicode=true in the connection string.

To use 4-byte UTF8 with Connector/J configure the MySQL server with character_set_server=utf8mb4. Connector/J will then use that setting as long as characterEncoding has not been set in the connection string. This is equivalent to autodetection of the character set. (Bug #58232)

按照MySQL官方手册提供的方法,MySQL JDBC驱动内部会在建立连接时发送SET NAMES utf8mb4给服务端,确保正确进行字符编码。 所以,本问题属于应用未按要求使用MySQL JDBC。但5.1.15可以插入4字节字符也是比较奇怪的事情。 mysql-connector官网的 change log中并且提交5.1.15~5.1.22之间有相关的改动。但是,通过比较代码发现,这部分逻辑确实发生了变更。

5.1.15

com\mysql\jdbc\ConnectionImpl.java:

private boolean configureClientCharacterSet(boolean dontCheckServerMatch)

throws SQLException

{

...

if(getEncoding() != null)

{

String mysqlEncodingName = CharsetMapping.getMysqlEncodingForJavaEncoding(getEncoding().toUpperCase(Locale.ENGLISH), this);

if(getUseOldUTF8Behavior())

mysqlEncodingName = "latin1";

if(dontCheckServerMatch || !characterSetNamesMatches(mysqlEncodingName))

execSQL(null, (new StringBuilder()).append("SET NAMES ").append(mysqlEncodingName).toString(), -1, null, 1003, 1007, false, database, null, false);

realJavaEncoding = getEncoding();

}

...

}

给CharsetMapping.getMysqlEncodingForJavaEncoding()传入的参数是UTF-8,对应的mysql的编码有2个,utf8和utf8mb4, 其中utf8mb4优先,所以这个函数返回的mysql编码是utf8mb4。即之后执行了SET NAMES utf8mb4

相关代码:

com\mysql\jdbc\CharsetMapping.java:

public static final String getMysqlEncodingForJavaEncoding(String javaEncodingUC, Connection conn)

throws SQLException

{

List mysqlEncodings = (List)JAVA_UC_TO_MYSQL_CHARSET_MAP.get(javaEncodingUC);

if(mysqlEncodings != null)

{

Iterator iter = mysqlEncodings.iterator();

VersionedStringProperty versionedProp = null;

do

{

if(!iter.hasNext())

break;

VersionedStringProperty propToCheck = (VersionedStringProperty)iter.next();

if(conn == null)

return propToCheck.toString();

if(versionedProp != null && !versionedProp.preferredValue && versionedProp.majorVersion == propToCheck.majorVersion && versionedProp.minorVersion == propToCheck.minorVersion && versionedProp.subminorVersion == propToCheck.subminorVersion)

return versionedProp.toString();

if(!propToCheck.isOkayForVersion(conn))

break;

if(propToCheck.preferredValue)

return propToCheck.toString();

versionedProp = propToCheck;

} while(true);

if(versionedProp != null)

return versionedProp.toString();

}

return null;

}

...

CHARSET_CONFIG.setProperty("javaToMysqlMappings", "US-ASCII =\t\t\tusa7,US-ASCII =\t\t\t>4.1.0 ascii,...

UTF-8 = \t\tutf8,UTF-8 =\t\t\t\t*> 5.5.2 utf8mb4,...");

注:上面的定义UTF-8 =\t\t\t\t*> 5.5.2 utf8mb4中的*代表有多个mysql编码对应于同一个Java编码时,该编码优先

5.1.22

com\mysql\jdbc\ConnectionImpl.java:

private boolean configureClientCharacterSet(boolean dontCheckServerMatch)

throws SQLException

{

...

if(getEncoding() != null)

{

String mysqlEncodingName = getServerCharacterEncoding();

if(getUseOldUTF8Behavior())

mysqlEncodingName = "latin1";

boolean ucs2 = false;

if("ucs2".equalsIgnoreCase(mysqlEncodingName) || "utf16".equalsIgnoreCase(mysqlEncodingName) || "utf32".equalsIgnoreCase(mysqlEncodingName))

{

mysqlEncodingName = "utf8";

ucs2 = true;

if(getCharacterSetResults() == null)

setCharacterSetResults("UTF-8");

}

if(dontCheckServerMatch || !characterSetNamesMatches(mysqlEncodingName) || ucs2)

execSQL(null, (new StringBuilder()).append("SET NAMES ").append(mysqlEncodingName).toString(), -1, null, 1003, 1007, false, database, null, false);

realJavaEncoding = getEncoding();

}

...

}

...

public String getServerCharacterEncoding()

{

if(io.versionMeetsMinimum(4, 1, 0))

{

String charset = (String)indexToCustomMysqlCharset.get(Integer.valueOf(io.serverCharsetIndex));

if(charset == null)

charset = (String)CharsetMapping.STATIC_INDEX_TO_MYSQL_CHARSET_MAP.get(Integer.valueOf(io.serverCharsetIndex));

return charset == null ? (String)serverVariables.get("character_set_server") : charset;

} else

{

return (String)serverVariables.get("character_set");

}

}

解决办法

一直使用旧版的5.1.15驱动不是一个好办法,因此在使用新版驱动时,采取以下措施之一解决这个问题。

参考官网的说明,修改my.cnf

character_set_server=utf8mb4

在应用中获取连接后执行下面的SQL

stmt.executeUpdate("set names utf8mb4")

补充

根据5.1.22的MySQL JDBC驱动代码,MySQL JDBC支持utf8mb4需要满足以下2个条件

1. MySQL系统变量`character_set_server`的值为utf8mb4

2. MySQL JDBC连接参数characterEncoding的值为以下值之一

- null

- UTF8

- UTF-8

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

主席树学习小结(POJ 2104)

在高中的时候就听到过主席树了,感觉非常高端,在寒假的时候 winter homework中有一题是查找区间第K大的树,当时就开始百度这种网上的博客,发现主席树看不懂,因为那个root[i],还有tx[x].l与tx[x].r是什么意思…

通过Spring Social推特StackExchange –第1部分

本文将介绍一个快速的附带项目-一个自动从各种Q&A StackExchange网站上发布热门问题的机器人,例如StackOverflow , ServerFault , SuperUser等。我们将为StackExchange API构建一个简单的客户端,然后进行设置使用Sprin…

下拉菜单

<!Doctype html> <html> <head> <meta charset"utf-8"> <title>下拉菜单</title> <style> *{ margin:0; padding:0; } ul{ list-style:none; overflow:hidden; background-color:#333; } li{ float:left; } li a,.drop…

51Nod 1753 相似子串

题目大意: 两个字符串相似定义为&#xff1a; 1.两个字符串长度相等 2.两个字符串对应位置上有且仅有至多一个位置所对应的字符不相同 给定一个字符串&#xff0c;每次询问两个子串在给定的规则下是否相似。给定的规则指每次给出一些等价关系&#xff0c;如‘a’b&#xff0c;‘…

deepin下Clion连接mysql_CLion如何添加依赖库 ? 需要把mysql/Connector c++放入 用cpp连接数据库...

目前我把下载的mysql/Connector 下载后放在了project内 但是报错信息如下报错信息如下In file included from /Users/wsgdrfz/study/c/Libary_System/librarySystem/sqlConnection.h:/Users/wsgdrfz/study/c/Libary_System/librarySystem/sqlFiles/include/mysql_connection.h7…

[开源JVM] yvm - 自制Java虚拟机

中文 | English | | | YVM是用C写的一个Java虚拟机&#xff0c;现在支持Java大部分功能&#xff0c;以及一个基于标记清除算法的并发垃圾回收器. 不过还有很多bug等待修复。 感兴趣的朋友pull request/fork/star吧。 Github repo https://github.com/racaljk/yvm 已支持语言…

MacOSX环境上的多个Java JDK

同样&#xff0c;这是在Mac&#xff08;OSX 10.8.x &#xff09;上配置Java开发环境的一个小技巧。 如果您现在真的开始&#xff0c;我建议您阅读我以前的文章之一 &#xff0c;这是一种快速&#xff0c;干净的方法&#xff08;我想&#xff09;来设置环境变量并开始Java编码。…

浏览器的标准模式和怪异模式

面试题之浏览器的标准模式和怪异模式 1.浏览器的标准模式和怪异模式到底是什么&#xff1f; 标准模式&#xff1a; 是浏览器按照W3C标准解析执行代码&#xff0c;这样用规定的语法去渲染&#xff0c;就可以兼容各个浏览器&#xff0c;保证以正确的形式展示网页。 怪异模式&…

printf函数输出超出int时怎么办

int、long、long long在printf中的格式 https://blog.csdn.net/fz_ywj/article/details/8107582 蓝桥杯 2796. BASIC-11 十六进制转十进制 从键盘输入一个不超过8位的正的十六进制数字符串&#xff0c;将它转换为正的十进制数后输出。   注&#xff1a;十六进制数中的10~15…

升级glibc的影响_Java 11 升级:“债务”“危机”

导读&#xff1a;AJDK11(阿里内部基于openJDK11的定制版本)在19年3月左右发布&#xff0c;到现在也快1年了&#xff0c;不过目前整体使用的面还是比较窄&#xff0c;特性被了解的也不是很多&#xff0c;Java11作为OpenJDK发布的LTS版本&#xff0c;对我们来说&#xff0c;还是需…

The processing instruction target matching [xX][mM][lL] is not allowed.

原因&#xff1a;xml标签 必须在第一行。 转载于:https://www.cnblogs.com/wangkang0320/p/7569100.html

周五尾盘上涨,配合周末消息,周一套人的经典实例

吸取中国软件的教训&#xff1a;注意这次是温柔的&#xff0c;只是收了个十字星&#xff0c;因为前期上涨后的需要缓和调整一下&#xff0c;而不是为了出逃。以后注意找个出逃的例子1、上周五尾盘上涨2、周末利好消息3、周一开盘高开4、高开后快速下探&#xff08;5分钟内&…

Spring Data,MongoDB和JSF集成教程

示例应用程序简介&#xff08;MongoShop产品目录&#xff09; 在学习完本教程之后&#xff0c;将构建具有以下功能要求的示例应用程序&#xff08;MongoShop产品目录&#xff09;&#xff1a; 1.搜索具有不同条件的产品&#xff08;例如&#xff0c;sku&#xff0c;产品类型&am…

表单元素

一、form form代表表单&#xff0c;功能&#xff1a;用于申明表单&#xff0c;定义采集数据的范围&#xff0c;也就是<form>和</form>里面包含的数据将被提交到服务器或者电子邮件里。<form> 标签用于为用户输入创建 HTML 表单。表单能够包含input元素&#…

本人用python刷题时的错误总结

本人新手&#xff0c;在leetcode刷题过程中出现过很多问题&#xff0c;也发现了很多方法&#xff0c;故在此总结&#xff0c;不定时更新。 1、在创建一个二维列表的时候&#xff0c;我之前会用 a [[0] * 5] * 5, 但是这样输出的结果往往会跟期待的不一样&#xff0c;我一直以为…

增加 jQueryValidate的手机号验证功能

1、通过addMethod增加手机号的验证方法 &#xff08;位置&#xff1a;和$(form).validate({}) 同级别&#xff09; //增加手机号验证规则$.validator.addMethod("isMobile", function(value, element) {var length value.length;var mobile /^(13[0-9]{9})|(18[0-9…

C++的STL中accumulate的用法

https://blog.csdn.net/u011499425/article/details/52756242

Hibernate继承:每个类层次结构的表

在本教程中&#xff0c;我们将看到如何在hibernate中实现继承。有3种方法可以在hibernate中实现继承。在本文中&#xff0c;我们将看到其中一种&#xff0c;即每个类层次结构一个表。 休眠中的继承&#xff1a; Java是面向对象的语言&#xff0c;继承是Java的主要功能之一。关…

锁 mysql_Mysql的锁(S锁和X锁的区别)

共享锁和排它锁Mysql的锁系统&#xff1a;shared lock 和 exclusive lock (共享锁和排它锁&#xff0c;也叫读锁和写锁&#xff0c;即read lock和write lock)读锁是共享的&#xff0c;或者说是相互不阻塞的写锁是排他的&#xff0c;一个写锁会阻塞其他的写锁和读锁在实际的数据…