天天看點

Spring Boot下Mysql資料庫的中文亂碼問題分析

引言: 今天的問題将圍繞Java寫入Mysql之時,中文在資料庫中程式設計亂碼的分析追蹤過程,以此來了解和優化分析解決問題的過程。

1.  開發環境描述

 Spring Boot 1.4.0.RELEASE, JDK 1.8, Mysql 5.7,  CentOS 7

2.  問題描述

   在Java代碼中,儲存中文到資料,發現在資料庫中顯示為???,這個是亂碼的表現, 剩下的問題是哪個環節出現了問題呢?

Spring Boot下Mysql資料庫的中文亂碼問題分析

3.  問題分析以及推理

    在整個環節中,産生亂碼的環節主要有以下幾個:java代碼, IDE, 代碼所在的系統, Mysql連接配接, 資料庫所在的作業系統,資料庫層面。這裡我們使用utf-8來做通用的編碼格式。

     接下來我們進行逐個分析與排查可能的問題:

      A:  IDE本身的編碼, 經過排查正确, utf-8.

Spring Boot下Mysql資料庫的中文亂碼問題分析

    B. 開發所使用的作業系統

         經過确認為windows 7的中文版,應該不是問題的根源。

    C.  Mysql的連接配接驅動

          目前使用的連接配接URL為: jdbc:log4jdbc:mysql://localhost:3306/mealsystem?useUnicode=true&characterEncoding=utf-8

          問号後面挂接的unicode編碼的支援,設定為utf-8.

     D.  資料庫所在的作業系統

[[email protected] ~]# lsb_release -a
LSB Version:    :core-4.1-amd64:core-4.1-noarch
Distributor ID: CentOS
Description:    CentOS Linux release 7.2.1511 (Core) 
Release:        7.2.1511
Codename:       Core
[[email protected] ~]# uname -a
Linux flybird 3.10.0-327.3.1.el7.x86_64 #1 SMP Wed Dec 9 14:09:15 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
[[email protected] ~]# cat /etc/redhat-release 
CentOS Linux release 7.2.1511 (Core) 
[[email protected] ~]# 
           

   E.  作業系統的編碼以及locale:

[[email protected] ~]# locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
           

   經過确認,沒有問題,都是遵守utf-8的格式。

   F.  資料庫中的表分析:

      資料庫表test,  表中5個字段,id, name, created_time, updated_time, version.

      其中表的encode如下, 确認為utf-8.

Spring Boot下Mysql資料庫的中文亂碼問題分析

      其中目标字段name的編碼格式:

Spring Boot下Mysql資料庫的中文亂碼問題分析

      故name本身的編碼沒有問題。

 3.  Spring Boot的Java代碼分析

      TestEntity的定義:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name="test")
public class TestEntity extends BaseEntity {
 
	private static final long serialVersionUID = -4437451262794760844L;

	@Column
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
           

  DAO的TestRepository.java的定義:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.rain.wx.meal.model.TestEntity;

@Repository
public interface TestRepository extends JpaRepository<TestEntity, Long> {

}
           

測試代碼:

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("dev")
public class TestEntityTest {

	@Autowired
	private TestRepository testRepo;
	
	@Test
	public void testEntity() {
		TestEntity test = new TestEntity();
		test.setName("我的天空");
		
		test = testRepo.save(test);
		
		test = testRepo.findOne(test.getId());
		System.out.println("tst info:" + test);
	}
}
           

   經過分析,由于IDE本身已經設定了UTF-8的編碼,故在代碼已經無需額外的轉碼,且在代碼層面已經進行了轉碼的測試,比如utf-8, gb2312, gbk, is08859_1等編碼,皆仍未亂碼。

4.   基于Mysql的用戶端的驗證分析

     基于workbench或者Navicat之類的用戶端工具,打開目标表test, 手動輸入中文資訊到test的name字段,儲存之後,重新查詢,發現仍為中文資訊。 基于代碼針對基于用戶端輸入的資訊,進行查詢發現,可以正常的查出中文資訊來。

     基于這個正确查詢出來的結果,可以确認從資料中的查詢是正确的;目前存在問題的路徑為寫入中文的過程。

5.  聚焦資料庫本身

      在之前排查完了作業系統的編碼之後,資料庫的編碼也需要排查一下:

Spring Boot下Mysql資料庫的中文亂碼問題分析

    忽然發現character_set_server的編碼是latin1, 原來問題在這樣; 在基本确認問題源頭之後,我們來看看如何解決。

6.  問題的解決方式

     修改character_set_server的encode:

     >> set global character_set_server = utf8.

     然後重新開機 mysqlServer之後,很不幸,竟然不生效。不知道問題出在哪裡。。。。。。

     那好吧,我們換一種方式來做吧,在/etc/my.cnf中進行初始化資料庫的encode:

[client]   # 新增用戶端的編碼
default-character-set=utf8

[mysql]   # 新增用戶端的編碼,預設
default-character-set=utf8

[mysqld]
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

# Recommended in standard MySQL setup
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES 

# 新增 關于character_set_server的編碼設定
init-connect='SET NAMES utf8'
character-set-server = utf8

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

           

這裡在mysql中新增了如下記錄,來初始化設定mysql資料庫伺服器的編碼:

init-connect='SET NAMES utf8'
character-set-server = utf8
           

然後,重新啟動mysql服務:

systemctl restart mysql
           

重新執行測試代碼,欣喜之中看到了預期中的結果:

2016-08-31 16:26:27.613  INFO 12556 --- [           main] jdbc.audit                               : 4. Connection.getWarnings() returned null
2016-08-31 16:26:27.614  INFO 12556 --- [           main] jdbc.audit                               : 4. Connection.clearWarnings() returned 
2016-08-31 16:26:27.615  INFO 12556 --- [           main] jdbc.audit                               : 4. Connection.clearWarnings() returned 
tst info:[email protected][
  name=我的天空
  id=7
  version=0
  createdTime=<null>
  updatedTime=<null>
]
2016-08-31 16:26:27.656  INFO 12556 --- [       Thread-2] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@71687585: startup date [Wed Aug 31 16:26:08 CST 2016]; root of context hierarchy
2016-08-31 16:26:27.670  INFO 12556 --- [       Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2016-08-31 16:26:27.677  INFO 12556 --- [       Thread-2] jdbc.connection                          : 1. Connection closed
2016-08-31 16:26:27.677  INFO 12556 --- [       Thread-2] jdbc.audit                               : 1. Connection.close() returned 
2016-08-31 16:26:27.679  INFO 12556 --- [       Thread-2] jdbc.connection                          : 2. Connection closed
2016-08-31 16:26:27.680  INFO 12556 --- [       Thread-2] jdbc.audit                               : 2. Connection.close() returned 
2016-08-31 16:26:27.680  INFO 12556 --- [       Thread-2] jdbc.connection                          : 3. Connection closed
2016-08-31 16:26:27.680  INFO 12556 --- [       Thread-2] jdbc.audit                               : 3. Connection.close() returned 
2016-08-31 16:26:27.682  INFO 12556 --- [       Thread-2] jdbc.connection                          : 5. Connection closed
2016-08-31 16:26:27.683  INFO 12556 --- [       Thread-2] jdbc.audit                               : 5. Connection.close() returned 
2016-08-31 16:26:27.684  INFO 12556 --- [       Thread-2] jdbc.audit                               : 4. PreparedStatement.close() returned 
2016-08-31 16:26:27.685  INFO 12556 --- [       Thread-2] jdbc.audit                               : 4. PreparedStatement.close() returned 
2016-08-31 16:26:27.685  INFO 12556 --- [       Thread-2] jdbc.connection                          : 4. Connection closed
2016-08-31 16:26:27.686  INFO 12556 --- [       Thread-2] jdbc.audit                               : 4. Connection.close() returned 
2016-08-31 16:26:27.687  INFO 12556 --- [       Thread-2] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closed
           

7. 參考資料

  •    http://stackoverflow.com/questions/3513773/change-mysql-default-character-set-to-utf-8-in-my-cnf
  •   http://www.cnblogs.com/-1185767500/articles/3106194.html
  •   https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_character_set_database