背景
在生产上发现一个接口数据怎么查都为空,做的日志记录,sql语句以及参数手动执行却能返回结果
排查
刚发现问题的时候,第一时间是通过日志去查看问题,模拟下核心点就如下
2024-01-24 14:10:03,912 DEBUG selectSQL:137 - ==> Preparing: SELECT COUNT(*) FROM a cwo WHERE cwo.`ques` REGEXP replace(' 3.* ',' ','')
2024-01-24 14:10:03,920 DEBUG selectSQL:137 - <== Total: 1
mapper对应的查询语句是这么写的
<select id="selectSQL" resultType="integer">
SELECT COUNT(*) FROM a cwo
<where>
<if test="param.ques!= null and param.ques.size() > 0">
<foreach collection="param.ques" item="que" separator=","
open="cwo.`ques` REGEXP replace('" close="',' ','')">
${que}
</foreach>
</if>
</where>
</select>
tips1:这里之所以是用
${}
而不是#{}
去写,是因为这里需要一个完整的字符串去执行正则(后面想了想,其实这个不是只要在逻辑里先拼接好不就行了吗?!(摔)),而占位符只会生成一个个的字符串对象,所以用了连接符去生成一个字符串
tips2:这里用replace(data, ’ ', ‘’)是因为拼接可能会出现多余的空格,导致regexp正则匹配失败
拿上面debug的sql去数据库执行,是有数量返回的,而调用接口,只会返回0
分析
既然打印日志发现不了问题,那就进行断点调试,这里就不说断点调试过程了,网上挺多的文章的,直接断点分析到SimpleExecutor.java
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行到这里的时候,stmt是带着sql完整语句的,可以查看自己生成的真正SQL语句(stmt.h.statement.delegate)
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
然后就看到了
HikariProxyPreparedStatement@1735946981 wrapping com.mysql.cj.jdbc.ClientPreparedStatement: SELECT COUNT(DISTINCT csr.cwo_id) FROM a cwo
WHERE cwo.`ques` REGEXP replace('
2.*
',' ','')
可以看到由于平时的小强迫症写法,导致生成的字符串是带了换行的,replace没能正确生成我们预期的正则表达式,导致匹配不到数据
tips
这里断点有个小技巧,idea点击断点,选择More,
然后选中你调用sql的位置断点,这样就能在直到执行你想要断点的位置时,才会进入SimpleExecutor
解决
既然是写法问题,那就修改mapper文件,这样就可以了
<select id="selectSQL" resultType="integer">
SELECT COUNT(*) FROM a cwo
<where>
<if test="param.ques!= null and param.ques.size() > 0">
<foreach collection="param.ques" item="que" separator=","
open="cwo.`ques` REGEXP replace('" close="',' ','')">${que}</foreach>
</if>
</where>
</select>
总结
平时写sql多多少少会遇到各种奇奇怪怪的现象,但是大多数能够通过打印的日志发现问题,如果坚信sql是对的的话,不妨断点确认下最终送给数据库的语句是怎样的(毕竟,各种插件在便利代码的同时,可能意外给你加上各种意料外的条件 😃