道理很简单,把gbk的汉字转换成iso编码存进数据库就可以了,读出来的时候把iso转换成gbk还原出原始的汉字。
ibatis可以自定义类型处理器,在这里面做编码转换再适合不过了!
sqlmap-config.xml:
User.xml这里就可以使用ChineseString这种数据类型了,往数据库读数据和写数据的时候会调用CnStringTypeHandler里面的:
getResult(ResultGetter getter)和setParameter(ParameterSetter setter, Object parameter);
edu.sdust.xujsh.test.type.ChineseString.java:
这么做,只要数据库服务器的编码方式兼容iso编码,存储中文都不会有问题。看上去很完美的解决方案,但是还有个问题,存储的数据的长度的变化。
看一下我们的mysql:
mysql> show variables like 'character%';
+--------------------------+------------------------------------+
| Variable_name | Value |
+--------------------------+------------------------------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | gbk |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | D:\programs\mysql5\share\charsets\ |
+--------------------------+------------------------------------+
8 rows in set (0.00 sec)
数据库是utf8编码的。
mysql> show create table user \G
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (
`id` int(11) DEFAULT NULL,
`name` varchar(10) DEFAULT NULL,
`birth` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTA
MP
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> select * from user where id =12345;
+-------+-------+---------------------+
| id | name | birth |
+-------+-------+---------------------+
| 12345 | ???ú | 2013-10-15 10:22:45 |
+-------+-------+---------------------+
1 row in set (0.00 sec)
mysql> select length(name) from user where id = 12345;
+--------------+
| length(name) |
+--------------+
| 8 |
+--------------+
1 row in set (0.00 sec)
“中国”的gbk编码是d6 d0 b9 fa,转化成了4个iso字符,在utf-8里面,0080 ~07FF之间的字符占2个字节,这4个字符都在这个范围内,共占8个字节。
而正常存储"中国"只需要6个字节,一个汉字在utf编码下是3个字节,如下:
mysql> set names gbk; --在客户端设置一下编码才能正常的插入中文
Query OK, 0 rows affected (0.00 sec)
mysql> insert into user values(123456,'中国',now());
Query OK, 1 row affected (0.04 sec)
mysql> select * from user where id = 123456;
+--------+------+---------------------+
| id | name | birth |
+--------+------+---------------------+
| 123456 | 中国 | 2013-10-15 10:39:18 |
+--------+------+---------------------+
1 row in set (0.00 sec)
mysql> select length(name) from user where id = 123456;
+--------------+
| length(name) |
+--------------+
| 6 |
+--------------+
1 row in set (0.00 sec)
可见,同样是存储“中国”两个汉字,原先要6个字节,现在变为8个字节。
mysql5里面,varchar(N),指的是N个字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放N个,最大大小是65532字节。
因此,原先我们可以用name varchar(2),现在必须用name varchar(4)。
ps:set names gbk 是让mysql server 和客户端之间的传输用gbk编码,存储在server上的还是utf8,在客户端查询时仍然需要gbk码,因为cmd命令行默认是gbk编码的。、
或者是这么搞:
mysql> set character_set_client=gbk;
Query OK, 0 rows affected (0.00 sec)
mysql> set character_set_results=gbk;
Query OK, 0 rows affected (0.00 sec)
参考文档:
ibatis可以自定义类型处理器,在这里面做编码转换再适合不过了!
sqlmap-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<settings useStatementNamespaces="true"/>
<typeHandler javaType="edu.sdust.xujsh.test.type.ChineseString"
callback="edu.sdust.xujsh.test.type.handler.CnStringTypeHandler" /><!--注意这里的自定义类型处理器-->
<!-- 使用JDBC的事务管理 -->
<transactionManager type="JDBC">
<!-- 数据源 -->
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="com.mysql.jdbc.Driver" />
<property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost:3306/test" />
<property name="JDBC.Username" value="root" />
<property name="JDBC.Password" value="123456" />
</dataSource>
</transactionManager>
<!-- 这里可以写多个实体的映射文件 -->
<sqlMap resource="User.xml" />
</sqlMapConfig>
User.xml:
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd">
<sqlMap namespace="User">
<typeAlias alias="user" type="com.xujsh.test.dto.User"/>
<resultMap id="userResult" class="user">
<result property="id" column="id" jdbcType="DECIMAL"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="birth" column="birth" jdbcType="TIMESTAMP"/>
</resultMap>
<insert id="insertUser" parameterClass="user"><![CDATA[
insert into USER (id,name,birth)
values (#id#,#name#,#birth#)
]]></insert>
<select id="getUserByUserId" resultMap="userResult" parameterClass="int"><![CDATA[
select id,name,birth
from USER
where id = #id#
]]></select>
</sqlMap>
User.xml这里就可以使用ChineseString这种数据类型了,往数据库读数据和写数据的时候会调用CnStringTypeHandler里面的:
getResult(ResultGetter getter)和setParameter(ParameterSetter setter, Object parameter);
edu.sdust.xujsh.test.type.ChineseString.java:
public class ChineseString {
private String value;//简单的对原始的字符串做一个包装
public ChineseString(){}
public ChineseString(String value){
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
edu.sdust.xujsh.test.type.handler.CnStringTypeHandler.java:
public class CnStringTypeHandler implements TypeHandlerCallback {
// 中文数据被保存到数据库所使用的字符集
private static final String STORE_CHARSET = "GBK";
// 系统使用的字符集
private String systemEncoding = "iso-8859-1";
/**
* 从数据库中取数据
*/
public Object getResult(ResultGetter getter) throws SQLException {
String value = getter.getString();
if (value == null) {
return null;
} else {
try {
return new ChineseString(new String(value.getBytes(systemEncoding), STORE_CHARSET));
} catch (UnsupportedEncodingException ue) {
return value;
}
}
}
/**
* 往数据库写数据
*/
public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
if (parameter != null) {
ChineseString value = (ChineseString) parameter;
if (value.getValue() != null) {
try {
setter.setString(new String(value.getValue().getBytes(STORE_CHARSET), systemEncoding));
} catch (UnsupportedEncodingException ue) {
setter.setString(value.getValue());
}
return;
}
}
setter.setNull(Types.VARCHAR);
}
public Object valueOf(String s) {
return s;
}
}
com.xujsh.test.dto.User.java:
public class User {
private int id;
private ChineseString name;
private Date birth;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ChineseString getName() {
return name;
}
public void setName(ChineseString name) {
this.name = name;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", birth=" + birth + "]";
}
}
com.xujsh.test.client.Main.java:
public class Main {
private static SqlMapClient sqlMapClient = null;
// 读取配置文件
static {
try {
InputStream in = Main.class.getClassLoader().getResourceAsStream("sqlmap-config.xml");
sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(in);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static User selectUserById(int id) throws Exception {
return (User) sqlMapClient.queryForObject("User.getUserByUserId", id);
}
public static void insertUser(User user)throws Exception {
sqlMapClient.insert("User.insertUser", user);
}
public static void main(String[] args) throws Exception {
User u = new User();
u.setId(123456);
u.setName(new ChineseString("中国"));
u.setBirth(new Date());
Main.insertUser(u);
u = Main.selectUserById(u.getId());
System.out.println(u);
}
}
这么做,只要数据库服务器的编码方式兼容iso编码,存储中文都不会有问题。看上去很完美的解决方案,但是还有个问题,存储的数据的长度的变化。
看一下我们的mysql:
mysql> show variables like 'character%';
+--------------------------+------------------------------------+
| Variable_name | Value |
+--------------------------+------------------------------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | gbk |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | D:\programs\mysql5\share\charsets\ |
+--------------------------+------------------------------------+
8 rows in set (0.00 sec)
数据库是utf8编码的。
mysql> show create table user \G
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (
`id` int(11) DEFAULT NULL,
`name` varchar(10) DEFAULT NULL,
`birth` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTA
MP
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> select * from user where id =12345;
+-------+-------+---------------------+
| id | name | birth |
+-------+-------+---------------------+
| 12345 | ???ú | 2013-10-15 10:22:45 |
+-------+-------+---------------------+
1 row in set (0.00 sec)
mysql> select length(name) from user where id = 12345;
+--------------+
| length(name) |
+--------------+
| 8 |
+--------------+
1 row in set (0.00 sec)
“中国”的gbk编码是d6 d0 b9 fa,转化成了4个iso字符,在utf-8里面,0080 ~07FF之间的字符占2个字节,这4个字符都在这个范围内,共占8个字节。
而正常存储"中国"只需要6个字节,一个汉字在utf编码下是3个字节,如下:
mysql> set names gbk; --在客户端设置一下编码才能正常的插入中文
Query OK, 0 rows affected (0.00 sec)
mysql> insert into user values(123456,'中国',now());
Query OK, 1 row affected (0.04 sec)
mysql> select * from user where id = 123456;
+--------+------+---------------------+
| id | name | birth |
+--------+------+---------------------+
| 123456 | 中国 | 2013-10-15 10:39:18 |
+--------+------+---------------------+
1 row in set (0.00 sec)
mysql> select length(name) from user where id = 123456;
+--------------+
| length(name) |
+--------------+
| 6 |
+--------------+
1 row in set (0.00 sec)
可见,同样是存储“中国”两个汉字,原先要6个字节,现在变为8个字节。
mysql5里面,varchar(N),指的是N个字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放N个,最大大小是65532字节。
因此,原先我们可以用name varchar(2),现在必须用name varchar(4)。
ps:set names gbk 是让mysql server 和客户端之间的传输用gbk编码,存储在server上的还是utf8,在客户端查询时仍然需要gbk码,因为cmd命令行默认是gbk编码的。、
或者是这么搞:
mysql> set character_set_client=gbk;
Query OK, 0 rows affected (0.00 sec)
mysql> set character_set_results=gbk;
Query OK, 0 rows affected (0.00 sec)
参考文档:
http://blog.csdn.net/lovingprince/article/details/2768849
http://www.cnblogs.com/doit8791/archive/2012/05/28/2522556.html
源码下载地址:http://download.csdn.net/detail/goldenfish1919/6403209