这两天在做数据同步项目的联调,测试过程中发现针对mysql的一些使用上存在一些问题,比如batch不起效果,编码问题,预编译失效等等。 这里总结一下,做一下记录,希望对遇到类似问题的有所帮助
官方文档: http://dev.mysql.com/doc/refman/4.1/en/connector-j-reference-charsets.html
网上针对mysql的中文编码问题,已经有不少文章进行介绍,大概步骤如下:
1. 设置my.cnf中, 设置default-character-set=utf8
2. 创建表时指定编码,create table {...} ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin ;
3. 客户端链接url上,指定编码参数: useUnicode=true&characterEncoding=UTF-8
因为项目是做一个数据同步项目,所以需要处理的是客户端的编码问题(server端的编码会由DBA搞定),刚开始时没设置客户端链接参数,在程序中手工的将源库(GBK),转成了目标库(UTF-8)的编码,构造了特定编码的SQL。
但是用构造出来的UTF-8编码的string流,通过jdbcTemplate执行的时候,目标库中针对中文字符出现了乱码。
大致猜测应该是在driver层面没有正确的送出数据流到目标数据库上,具体编码处理代码如下:
类:com.mysql.jdbc.ConnectionImpl
方法: checkServerEncoding()
private void checkServerEncoding() throws SQLException { if (getUseUnicode() && (getEncoding() != null)) { //位置1 // spec'd by client, don't map return; } String serverEncoding = (String) this.serverVariables.get("character_set"); if (serverEncoding == null) { // must be 4.1.1 or newer? serverEncoding = (String) this.serverVariables.get("character_set_server"); } String mappedServerEncoding = null; if (serverEncoding != null) { mappedServerEncoding = CharsetMapping .getJavaEncodingForMysqlEncoding(serverEncoding.toUpperCase(Locale.ENGLISH), this); } // // First check if we can do the encoding ourselves // if (!getUseUnicode() && (mappedServerEncoding != null)) { SingleByteCharsetConverter converter = getCharsetConverter(mappedServerEncoding); if (converter != null) { // we know how to convert this ourselves setUseUnicode(true); // force the issue setEncoding(mappedServerEncoding); return; } } }
说明:
1. 首先会校验useUnicode和characterEncoding参数的设置(即为url中的参数设置),如果有则直接返回,不去自动猜测db server的编码
2. 读取mysql variables数据,提取character_set和character_set_server的编码做为characterEncoding编码
3. 基于characterEncoding构造了SingleByteCharsetConverter进行编码转化,最终录入到目标库
原理搞清楚了,基本这编码问题也就很容易解决了。
1. check下mysql variables变量:
命令: show variables like 'character\_set\_%';
结果:
character_set_server编码为latin1 , 而我数据表的编码为utf-8,这也就难怪出现乱码了,最后设置下根据不同的数据表设置characterEncoding即可。
相关文章: http://blog.csdn.net/axman/article/details/6913527
具体的参数设置,这里面已经写的比较详细了,我就简单的从driver层面来给大家介绍下。
类:com.mysql.jdbc.ConnectionImpl
方法: initializePropsFromServer()
// // Users can turn off detection of server-side prepared statements // if (getUseServerPreparedStmts() && versionMeetsMinimum(4, 1, 0)) { this.useServerPreparedStmts = true; if (versionMeetsMinimum(5, 0, 0) && !versionMeetsMinimum(5, 0, 3)) { this.useServerPreparedStmts = false; // 4.1.2+ style prepared // statements // don't work on these versions } }
说明:
1. getUseServerPreparedStmts()主要是从connection properties中提取useServerPrepStmts变量的设置(useServerPrepStmts=true/false)
2. prepareStatement使用代码,也是在ConnectionImpl.prepareStatement方法中
总结一下,注意几个参数即可:
mysql默认关闭了batch处理,需要设置rewriteBatchedStatements=true参数进行打开。
使用代码:com.mysql.jdbc.StatementImpl,com.mysql.jdbc.PreparedStatement的executeBatch()方法
总结一下,主要是几个参数的使用:
BasicDataSource dbcpDs = new BasicDataSource(); dbcpDs.addConnectionProperty("useServerPrepStmts", "true"); dbcpDs.addConnectionProperty("rewriteBatchedStatements", "true"); dbcpDs.addConnectionProperty("characterEncoding", encoding);