连接中的字符集注意有一下几个部分组成:
- 表示连接中服务器端的字符集character_set_connection和校对规则collation_connection
- 表示连接中客户端的字符集character_set_client
- 表示连接中返回给客户端的数据的字符集character_set_results ,包括结果数据,以及列名、报错信息等元数据
其中,collation_connection 主要对字符串字面量有效,对列值不生效,因为字符列有自己的校对规则。
客户端发送语句到服务端执行的时候,服务器端会将语句从字符集 character_set_client 转成character_set_connection。返回数据的时候,会将字符集转成character_set_results变量表示的字符集。
character_set_results
默认情况下,character_set_results 是没有值的,这个时候返回元数据信息的字符集看的是变量character_set_system。
用一个utf8mb3不能表示的中文字来看character_set_results 的作用:
create table t_character_set_result(
a varchar(30) character set utf8mb4
);
insert into t_character_set_result values('𢓭');
以utf8mb3字符集查看插入的值:
set character_set_results = 'utf8mb3';
select * from t_character_set_result;
a|
-+
?|
这时候单独改变character_set_client 为utf8mb4,并不会正常显示,说明返回数据的字符集的确看的是character_set_results 。
set character_set_client = 'utf8mb4';
select * from t_character_set_result;
接着将character_set_results改为null或者utf8mb4就可以正常显示
set character_set_results = null;
select * from t_character_set_result;
将character_set_results设为gb18030字符集,依然可以正常显示,这是因为正确发生了转换。同理也可以想象character_set_client 与character_set_connection之间的转换。重要的是转换的前后的字符集都可以表示对应的数据。
character_set_client
看一个例子:
connection.prepareStatement("set character_set_client = gb18030;").execute();
PreparedStatement preparedStatement = connection.prepareStatement("select '茶馆','风华'");
ResultSet resultSet = preparedStatement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
System.out.print(metaData.getColumnName(1));
System.out.print(",");
System.out.println( new String(metaData.getColumnName(1).getBytes("gb18030"), "utf-8"));
System.out.println("================");
while (resultSet.next()) {
String string = resultSet.getString(1);
System.out.print(string);
System.out.print(",");
System.out.println(new String(string.getBytes("gb18030"), "utf-8"));
}
鑼堕,茶馆
================
鑼堕,茶馆
这里指定的客户端的字符集是gb18030,而Java的默认字符集是UTF-8。需要转换一下。
元数据与值的字符集处理不完全相同,如果在上面的例子中一开始再加一句:
connection.prepareStatement("set character_set_results = gb18030;").execute();
结果变了,元数据已经是正确的UTF-8字符集:
茶馆,���
================
鑼堕,茶馆
另外一个注意的是,SQL中的字符串字面量因为需要从客户端的发到服务器,再发回来。但是数据库表里面的值不同,不需要多余转换:
connection.prepareStatement("set character_set_results = gb18030;")
.execute();
connection.prepareStatement("set character_set_client = gb18030;")
.execute();
PreparedStatement preparedStatement = connection.prepareStatement("select * from t_character_set_result;");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
String string = resultSet.getString(1);
System.out.print(string);
System.out.print(",");
System.out.println(new String(string.getBytes("gb18030"), "utf-8"));
}
𢓭,�9�9