深入理解Java和MySQL亂碼問題

來源:互聯網
上載者:User

最近我們使用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資料來源,那麼記得把’&’替換成’&’。

 

最後,當再次啟動伺服器時,探索資料庫中終於出現了正確的字元。

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.