日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

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

發布時間:2023/12/1 数据库 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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

總結

以上是生活随笔為你收集整理的mysql 4字节utf8_MySQL 4字节utf8字符更新失败一例的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。