一、问题

项目中一直使用Mybatis,通过Mybatis Log Free插件打印执行的SQL语句,再抓出来执行或者检查逻辑,一直还挺好用的。

最近遇到一个问题,就是SQL参数值中如果包含了括号,生成的SQL语句参数替换后字符串外面没有‘’,造成执行异常。简单来说这种参数没有当作字符串值来替换。

比如对于SQL语句UPDATE mp_user SET name=? WHERE id=? AND name=?,如果name值为stone(sh),预期得到的SQL为UPDATE mp_user SET name=null WHERE id=null AND name='stone(sh)',实际得到的是UPDATE mp_user SET name=null WHERE id=null AND name=stone(sh)

这个参数少时也没啥问题,手工替换一下。但是遇到参数比较多都需要手工替换就有点麻烦。

二、原因

这其实是插件的一个bug。看问题描述应该是未识别带括号的字符值类型。

通过IDEA插件中github链接直接找源码翻了一下。

原作者单测中有相关逻辑,src/test/java/com/starxg/mybatislog/MyBatisLogConsoleFilterTest.java,加入问题场景很容易复现。

Bug原因也很简单,类src/main/java/com/starxg/mybatislog/MyBatisLogConsoleFilter.java中函数parseParams逻辑不支持参数值带括号。会把找到的第一个括号中的值作为参数类型来解析,因为括号中不是有效类型因此默认原样输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 static Queue<Map.Entry<String, String>> parseParams(String line) {
line = StringUtils.removeEnd(line, "\n");

final String[] strings = StringUtils.splitByWholeSeparator(line, ", ");
final Queue<Map.Entry<String, String>> queue = new ArrayDeque<>(strings.length);

for (String s : strings) {
String value = StringUtils.substringBeforeLast(s, "(");
// 下面代码中原作者会把的第一个括号中的值作为参数类型。这里修复方法是把最后一个括号中的值作为参数类型
//String type = StringUtils.substringBetween(s, "(", ")");
int lastOpen = s.lastIndexOf('(');
int lastClose = s.lastIndexOf(')');
String type = lastOpen != -1 && lastClose != -1 && lastOpen < lastClose ? s.substring(lastOpen + 1, lastClose) : null;
if (StringUtils.isEmpty(type)) {
queue.offer(new AbstractMap.SimpleEntry<>(value, null));
} else {
queue.offer(new AbstractMap.SimpleEntry<>(value, type));
}
}

return queue;
}

这里修复方法也很简单,把最后一个括号中的值作为参数类型来解析即可。

三、后记

从github README看,这个插件原作者已经把这个插件从开源转收费了,不确定开源版本是否会继续维护和接受代码修复PR。如果其他同学需要临时升级的可以联系我获取升级zip包来修复。