背景
由于项目需要模糊查询,所以需要使用 LIKE
子句。LIKE
子句需要使用 %
作为占位符,所以需要将 %
与字段值连接到一起。Mybatis 下大概有如下几种方式实现:①使用 $ 占位符,②使用数据库函数 concat
,③使用 Mybatis 的动态 sql 标签 <bind> ,下面会简单介绍下前两种的用法和缺陷,然后重点介绍第三种,如何使用以及存在的坑。
使用 $
代码如下:
@Select({"<script>",
" SELECT * FROM SSS WHERE STATUS=1 ",
" <if test='keyWord != null'>",
" AND `NAME` LIKE '%${keyWord}%'",
" </if>",
"<if test='roleId != null'>",
" AND ROLE_ID = #{roleId} ",
"</if>",
"</script>"})
List<SellerDo> querySellers(SellerQuery sellerQuery);
弊端:使用 $ 作为占位符,会存在 sql 注入问题,生产环境中应当避免使用
使用数据库函数 concat
concat
属于数据库函数,MySQL 和 Oracle 都支持,用于字符串连接,而且可以使用 #
作为占位符,防止 SQL 注入。
代码如下
@Select({"<script>",
" SELECT * FROM SSS WHERE STATUS=1 ",
" <if test='keyWord != null'>",
" AND `NAME` LIKE concat('%', #{keyWord}, '%')",
" </if>",
"<if test='roleId != null'>",
" AND ROLE_ID = #{roleId} ",
"</if>",
"</script>"})
List<SellerDo> querySellers(SellerQuery sellerQuery);
弊端:有些公司并不推荐使用数据库函数,因为可能会切换数据库,如果切换到一个不支持 concat
函数的数据库就尴尬了。
使用 Mybatis 的动态 sql 标签 <bind>
bind
元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。使用 bind
标签就可以对某个字段进行'封装',比如给上面的 keyWord
字段两端各加一个百分号,如下:
<bind name="keyWord",value="'%'+keyWord+'%'"/>
则模糊查询可以使用 <bind> 优化一下:
@Select({ "<script>",
" SELECT * FROM SSS WHERE STATUS=1 ",
"<bind name='keyWord' value=\" '%' + keyWord + '%' \"/>",
" <if test='keyWord != null'>",
" AND `NAME` LIKE #{keyWord}",
"</if>",
"<if test='roleId != null'>",
" AND ROLE_ID = #{roleId} ",
"</if>",
"</script>"})
List<SellerDo> querySellers(SellerQuery sellerQuery);
这种方式就比使用 $
占位符安全,因为 #
占位符通过PreparedStatement
预编译的,可以防止 SQL 注入,而且即使更换数据库也同样没有问题,这是目前最好的一种方式。
<bind>坑点
场景
:mapper 方法中只有一个参数且是对象
类型时,可以不使用 @Param
注解声明变量,其它情况就必须使用 @Param
注解。在这里我还拿上文例子举例,即使 querySellers
方法只有一个变量且为 Java Bean 类型但是我同样可以使用@Param
修饰,代码如下:
@Select({ "<script>",
" SELECT * FROM SSS WHERE STATUS=1 ",
"<bind name='query.keyWord' value=\" '%' + query.keyWord + '%' \"/>",
" <if test='query.keyWord != null'>",
" AND `NAME` LIKE #{query.keyWord}",
"</if>",
"<if test='query.roleId != null'>",
" AND ROLE_ID = #{query.roleId} ",
"</if>",
"</script>"})
List<SellerDo> querySellers(@Param("query") SellerQuery sellerQuery);
调用代码和测试代码:
@Service
public class SellerServiceImpl implements SellerService {
@Autowired
private SellerDoMapper sellerDoMapper;
@Override
public void querySellers(SellerQuery sellerQuery) {
sellerDoMapper.querySellers(sellerQuery)
.forEach(System.out::println);
}
}
---------------------------------------------------------------------
@SpringBootTest
public class SellerServiceTest {
@Autowired
private SellerService sellerService;
@Test
public void querySellers() {
SellerQuery sellerQuery = new SellerQuery();
sellerQuery.setKeyWord("nihao");
// roleId 有值,注意
sellerQuery.setRoleId(4L);
sellerService.querySellers(sellerQuery);
}
}
执行测试代码,打印如下:

这个问题困扰了我几个小时,原因下个专题再讨论,会涉及到 Mybatis 的底层,解决办法就是不加 @Param
注解,如果有多个参数,则放在一个对象当做参数,这是我目前的解法。
后记
本文介绍了使用 Mybatis 开发模糊查询的三种方法,分别介绍其用法以及弊端,大家可以按照自己的业务需求选择。读完点个赞吧。
网友评论