問題描述:
1. 對於一些中文繁體字符select出來出現亂碼,出問題的繁體字如:燈、龍等
環境描述:
數據庫編碼:
+--------------------------+----------------------------------------+
| Variable_name | Value |
+--------------------------+----------------------------------------+
| auto_increment_offset | 1 |
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | D:/Program Files/mysql/share/charsets/ |
+--------------------------+----------------------------------------+
數據庫表編碼:也同意使用latin1編碼方式
由於數據庫由DBA負責,並且庫結構爲了保持一致(我們使用備份庫),從而不能修改數據庫編碼
問題排查:
1.mysql 的jdbc驅動源代碼拷貝下來DEBUG,最終發現了問題根源在驅動中CharSetMapping.class該類中的getJavaEncodingForMysqlEncoding(String mysqlEncoding,Connection conn)方法,該方法源代碼如下:
public final static String getJavaEncodingForMysqlEncoding(String mysqlEncoding, Connection conn) throws SQLException { if (conn != null && conn.versionMeetsMinimum(4, 1, 0) && "latin1".equalsIgnoreCase(mysqlEncoding)) { return "Cp1252"; } return (String) MYSQL_TO_JAVA_CHARSET_MAP.get(mysqlEncoding); }
這裏Latin1編碼就是iso-8859-1編碼。問題就出在這裏,mysql驅動對Latin1編碼做了特殊處理,轉爲cp1252,但cp1252依然屬於Latin1系編碼,故顯示中文依然會存在亂碼,故需要在GBKstring中轉化cp1252.
這麼做了以後,發現我看到的中文都不再亂碼OK,包括一些繁體字和火星文,大功告成了。
過了一天,我們測試給我反饋結果,說一些繁體字依然存在亂碼,比如“燈、龍”等,在頁面上顯示“?”,究竟哪兒出了問題?繼續DEBUG,
發現普通漢字從Latin1轉碼爲cp1252後的byte array中的數據中,用兩個字節表示一個漢字時,能夠在GBK編碼映射表中找到byte array對應的2字節數據,而“燈、龍”兩個繁體字轉cp1252後,其對應的byte array中的2字節數據無法再GBK編碼中找到(既GBK中無法找到該2字節數據對應的漢子),從而出現“?”。
故問題應該就出現在這裏,既從latin1->cp1252->gbk這樣一個過程會出現以下編碼數據丟失。從而解決方案也是很明顯的:
既:去掉中間轉cp1252的步驟,直接將Latin1 轉gbk,同時gbkString中不處理,將上面代碼修改爲:
public final static String getJavaEncodingForMysqlEncoding(String mysqlEncoding, Connection conn) throws SQLException { if (conn != null && conn.versionMeetsMinimum(4, 1, 0) && "latin1".equalsIgnoreCase(mysqlEncoding)) { return "gbk"; } return (String) MYSQL_TO_JAVA_CHARSET_MAP.get(mysqlEncoding); }
再一測試,問題解決!