问题来源
刚学习springboot不久,在测试DAO层时,发现怎么测试都返回的空集,然而直接在sql console中能够正确返回结果。源代码如下:
DAO层
@Select("select * from tb_book where ${condition} like " +
"concat('%',#{conditionKey},'%') limit #{pageStart}, " +
"#{pageSize}")
ArrayList getByCondition(int pageStart, int pageSize, String condition, String conditionKey);
DAO测试类
@Test
void testGetByCondition(){
String conditionKey = "java" ;
System.out.println(bookMapper.getByCondition(0, 4, "name", conditionKey));
}
正常情况下,通过console能够输出两条结果,但是测试时却是空集,却没有报错,到底咋回事。在网上怎么找都找不到相关类似的问题,最后在stackoverflow上找到一个靠谱的答案,把我指引向mybatis官方文档。仔细研究之后发现,其实问题就出在最最基础的字符串占位符上,也就是#{}
和${}
的问题。
#{}
并不是可以无脑替换所有的数据~~~~
文档中是这么写的:
By default, using the#{}
syntax will cause MyBatis to generatePreparedStatement
properties and set the values safely against the PreparedStatement parameters (e.g. ?). While this is safer, faster and almost always preferred, sometimes you just want to directly inject an unmodified string into the SQL Statement. For example, for ORDER BY, you might use something like this:ORDER BY ${columnName}
. Here MyBatis won't modify or escape the string.
也就是说在运行时,我们代码中的 where #{condition}
被放进了预编译的结果中,导致在解析时,被认为是 where "name" ...
, 这样显然不对,但是程序没有报错,sql日志也没有任何线索,所以这个问题可以说是非常难发现了。
解决方案
知道是占位符的问题之后,解决起来比较简单了,就是不用#{}
, 改用${}
。虽然可能会有sql注入的问题,但是这里也是无奈之举,最好的办法还是不要偷懒,分成两个或者多个sql语句来写会比较好。如果有什么更好的方案,欢迎讨论~~