今天发现一个MySQL驱动包执行in语句的一个bug,也许会有很多人还不知道,那么跟大家分享一下。
在使用dbutils执行sql语句的时候,遇到这样的sql:
select * from 表名 where 字段名 in (?)
如果你是这样写的,那肯定查询不到你想要的结果,验证此问题只在mysql上存在,如果传进去的是一个字符串,那么你将得到一个这样的一个预编译sql:
select * from 表名 where 字段名 in ('真实值,真实值...');
这样只会查询出在in里面第一个真实值匹配的结果。
下面代码说明
更改前我的代码
List articleIds = ArticleTag.INSTANCE.findByTagId(tagId);
StringBuilder sb = new StringBuilder();
for (Long id : articleIds) {
sb.append(id + ",");
}
String idString = null;
//去掉末尾的","
if (sb.length() > 1) {
idString = sb.substring(0, sb.length() - 1).toString();
} else {
return new ArrayList(0);
}
List ids = getIds("trash = 0 AND status = '" + Article.Status.PUBLISH
+ "' AND id in (?) ORDER BY id DESC",
idString);
上面是执行查询的代码,参数idString是一个字符串,getIds()方法调用底层QueryRunner的query();
查询底层实现看下dbutils的关于QueryRunner的query源码(源码太多,挑重点看一下):
stmt = this.prepareStatement(conn, sql);
this.fillStatement(stmt, params);
rs = this.wrap(stmt.executeQuery());
result = rsh.handle(rs);
在使用fillStatement设置参数的时候,执行下面源码(依然挑重点)
if(!var14) {
var15 = null;
parameterAsBytes = new StringBuilder(x.length() + 2);
parameterAsBytes.append('\'');
parameterAsBytes.append(x);
parameterAsBytes.append('\'');
if(!this.isLoadDataQuery) {
var16 = StringUtils.getBytes(parameterAsBytes.toString(), this.charConverter, this.charEncoding, this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), this.getExceptionInterceptor());
} else {
var16 = StringUtils.getBytes(parameterAsBytes.toString());
}
我们可以看到底层会将你传递进来的参数加上’’,并没有针对区分,所以SQL语句如上面我们说的in(’真实值,真实值…’)。这就导致了查询数据错误。
网上查询了说传数组进去,仔细想想也不靠谱。后来发现只能将?作为变量拼接进sql才能解决。
更改后代码:
List articleIds = ArticleTag.INSTANCE.findByTagId(tagId);
StringBuilder sb = new StringBuilder();
StringBuffer params = new StringBuffer();
for (Long id : articleIds) {
sb.append(id + ",");
params.append("?,");
}
String idString = null;
String paramsStr = null;
String[] str = new String[sb.length()];
//去掉末尾的","
if (sb.length() > 1) {
idString = sb.substring(0, sb.length() - 1).toString();
paramsStr = params.substring(0, params.length() - 1).toString();
str = idString.split(",");
} else {
return new ArrayList(0);
}
List ids = getIds("trash = 0 AND status = '" + Article.Status.PUBLISH
+ "' AND id in ("+paramsStr+") ORDER BY id DESC",
str);
将?也作为变量根据参数的个数动态拼接到sql里,这样问题就解决了,当然这也只是我的做法,如果大家有更好的做法,希望留言互相讨论,也希望这个bug能够修复。
转载地址:http://www.rivergo.cn/view/86