最近我們使用tomcat和MySQL搭建了一個Java Web伺服器,並將遊戲的伺服器邏輯部署在該伺服器上。
遊戲上線後不久,我們探索資料庫中出現了大量的亂碼。這是個非常嚴重的問題,因此必須馬上解決。但是問題出在什麼地方呢,根據分析,亂碼只可能出現在兩個時間點:
1. 將資料從用戶端傳送到伺服器的時候。
2. 伺服器將資料存放區到資料庫的時候。
經過調試之後,我們探索服務器輸出的資料是正常的,因此亂碼問題只能發生在儲存入庫的時候。
既然是MySQL資料庫編碼的問題,那事情就相對好辦了,首先,輸入命令:
mysql>show variables like '%char%';
+--------------------------+----------------+
|Variable_name | Value |
+--------------------------+----------------+
|character_set_client | utf8 |
| character_set_connection| utf8 |
|character_set_database | latin1 |
|character_set_filesystem | binary |
|character_set_results | utf8 |
|character_set_server | latin1 |
|character_set_system | utf8 |
+--------------------------+----------------+
上述命令用於顯示當前MySQL用到的資料庫編碼。當看到這麼多項資料編碼之後,腦袋都有點大,沒辦法,只能硬著頭皮一個一個地把這些參數搞明白。
l character_set_server:建立資料庫時,如果沒有指定字元編碼,那麼系統將使用character_set_server的值作為預設值。
l character_set_database:建立表時,如果沒有指定字元編碼,那麼系統將使用character_set_database的值作為預設值。
l character_set_client:定義了MySQL用戶端所發送資料的編碼。
l character_set_connection:當MySQL伺服器接收到用戶端發送過來的資料之後,會將這些資料轉化成character_set_connection所指定的編碼。
l character_set_results:MySQL伺服器返回查詢結果所使用的字元編碼。
知道這些參數的含義之後,就沒有那麼茫然無助了,因此腦海中回顧下資料存放區入庫的過程。
通過MySQL用戶端執行插入的過程如下,MySQL用戶端將使用者輸入的資料編碼成character_set_client,發送到伺服器,伺服器接收到之後,將其轉化成character_set_connection,然後伺服器再將這些資料存放區成character_set_table(上面這些參數不包含這個,即如果建表時沒有指定,該值即為character_set_database)。
回憶一下,發現當時建表的時候並沒有主動在建表語句後面指定字元編碼,那麼根據上面的描述,資料庫的表就會使用character_set_database所指定的字元編碼,即latin1。由於UTF-8編碼的中文無法儲存成latin1,因此在往latin1編碼的表中儲存UTF-8編碼的中文時,資料庫會將無法識別的字元變成?,並且這個過程是無法復原的。
為了證明這一點,我試著通過MySQL用戶端往表中插入中文,結果插入失敗。因此我決定將表的字元編碼修改成UTF-8,看看是否能往表中插入中文,執行如下語句之後:
mysql> ALTER TABLE `tableName` DEFAULTCHARACTER SET utf8 COLLATE utf8_general_ci
表的字元編碼被修改成了UTF-8,這時候發現也能往表中插入中文了。
當時以為事情到這裡就結束了,可是當我們啟動tomcat進行測試之後,探索資料庫中存入的依然是亂碼。既然MySQL用戶端都能插入中文,為啥使用JDBC就不能呢?很有可能JDBC並不使用character_set_client來編碼用戶端資料。
翻閱了無數文檔之後,發現如果不在JDBC URL中通過characterEncoding屬性指定串連編碼,JDBC Driver會使用character_set_server作為串連的編碼,在本案例中,即使用latin1。既然原因找到了,那問題就比較好解決了。
將JDBC URL修改成jdbc:mysql://localhost/some_db?useUnicode=yes
&characterEncoding=UTF-8即可,如果使用的是tomcat資料來源,那麼記得把’&’替換成’&’。
最後,當再次啟動伺服器時,探索資料庫中終於出現了正確的字元。