方式一
在MyBatis中的第一种写法:
1 2 3 4 | <!--有sql注入问题--> < select id = "findUserByLikeName1" parameterType = "java.lang.String" resultMap = "user" > select * from t_user where name like '%${name}%' </ select > |
这种会有sql注入的问题,需要明白在 MyBatis中 $ 和 # 使用的区别。这种写法也不能加jdbcType=VARCHAR,否则也会报错。
做了个简单的测试:
1 2 3 4 5 6 7 8 9 10 11 | @Test public void findUserByLikeName1(){ List<User> test = userMapper.findUserByLikeName1( "Cloud" ); //select * from t_user where name like '%Cloud%' System.out.println(test.size()); // 查出一条 List<User> test1 = userMapper.findUserByLikeName1( "' or '1=1" ); //select * from t_user where name like '%' or '1=1%' // 分析: '1=1%' 成立 System.out.println(test1.size()); // 查出了全部数据 } |
注意:排序的字段也容易出现这个问题,在使用的时候也一定要注意。
1 | order by ${orderBy} |
第一种方式在实际开发过程中千万要注意,不要写成这样了。
方式二
在MyBatis中的第二种写法:
1 2 3 4 | <!--直接在代码中拼接%,不存在sql注入--> < select id = "findUserByLikeName2" parameterType = "java.lang.String" resultMap = "user" > select * from t_user where name like #{name,jdbcType=VARCHAR} </ select > |
在代码中加上%
。
1 2 3 4 5 6 7 8 | @Test public void findUserByLikeName2(){ String name = "Cloud" ; List<User> test = userMapper.findUserByLikeName2( "%" +name+ "%" ); // select * from t_user where name like ? // %Cloud%(String) System.out.println(test.size()); } |
这种方式在一些项目中也会看到。如果没有使用如MyBatis等ORM框架,直接写sql查询就这样拼接了。
【推荐】方式三
在MyBatis中的第三种写法:
1 2 3 4 | <!--concat Mysql和 Oracle区别 ,不存在sql注入--> < select id = "findUserByLikeName3" parameterType = "java.lang.String" resultMap = "user" > select * from t_user where name like concat('%',#{name,jdbcType=VARCHAR},'%') </ select > |
测试:
1 2 3 4 5 6 7 8 | @Test public void findUserByLikeName3(){ String name = "Cloud" ; List<User> test = userMapper.findUserByLikeName3(name); // select * from t_user where name like concat('%',?,'%') // Cloud(String) System.out.println(test.size()); } |
在实际开发中推荐使用这种方式。
小注意
当使用方式三的时候,如果查询的关键字就是%
,那情况会是什么?初始化数据中name
有9条数据中包含%
。
查询的sql如下:
1 | select * from t_user where name like concat(’%’,’%’,’%’) |
查出来全部的数据,并不是只包含了%
的数据,如果查询_
也是一样的。
那这种情况肯定是不满足查询需求的,则需要调整。
①在代码中进行转义
1 2 3 4 5 6 7 8 9 | @Test public void findUserByLikeName3(){ String name = "%" ; name = name.replaceAll( "_" , "\\\\_" ); name = name.replaceAll( "%" , "\\\\%" ); List<User> test = userMapper.findUserByLikeName3(name); System.out.println(test.size()); } |
②使用ESCAPE
1 2 3 | < select id = "findUserByLikeName4" parameterType = "java.lang.String" resultMap = "user" > select * from t_user where name like concat('%',#{name,jdbcType=VARCHAR},'%') ESCAPE '/' </ select > |
测试:
1 2 3 4 5 6 7 8 9 | @Test public void findUserByLikeName4(){ // replaceAll("%", "/%").replaceAll("_", "/_") String name = "%" ; List<User> test = userMapper.findUserByLikeName4(name); System.out.println(test.size()); // 查到全部 List<User> test1 = userMapper.findUserByLikeName4( "/" +name); System.out.println(test1.size()); //查到匹配%的记录 } |
这两种本质都是对查询的关键字进行了处理,这种处理在代码中可以使用拦截器或者AOP等技术统一处理。
小总结
推荐使用第三种方式进行模糊查询
参考:
工作中的参考代码:
1.控制器
1 2 3 4 5 6 7 8 9 10 | @RequestMapping (UrlConst.User_admin) public String index(Model model, UserAdminForm userAdminForm) { // 用户列表 UserAdminForm userAdminForm1 = new UserAdminForm(); BeanUtil.copyProperties(userAdminForm, userAdminForm1); if (ObjectUtil.isNotEmpty(userAdminForm1)) { userAdminForm1.setAccount(DbHelper.escapeLike(userAdminForm1.getAccount())); userAdminForm1.setRealname(DbHelper.escapeLike(userAdminForm1.getRealname())); } Page<UserPO> userPageList = userMapper.getUserPageList(userAdminForm1, new Page<>(userAdminForm.getPageNum(), userAdminForm.getPageSize())); |
2.UserMapper.class
1 2 3 | public interface UserMapper extends BaseMapper<User> { Page<UserPO> getUserPageList(UserAdminForm userAdminForm, Page<UserPO> page); } |
3.UserMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <? xml version = "1.0" encoding = "UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> < mapper namespace = "com.wanma.framework_web.mapper.UserMapper" > < select id = "getUserPageList" resultType = "com.wanma.framework_web.model.po.UserPO" > SELECT A.id, A.account, A.realname, B.role_names FROM user A LEFT JOIN user_ext B ON A.id = B.user_id < where > < if test = "userAdminForm.account!=null and userAdminForm.account!=''" > AND A.account LIKE CONCAT('%', #{userAdminForm.account, jdbcType=VARCHAR},'%') </ if > < if test = "userAdminForm.realname!=null and userAdminForm.realname!=''" > AND A.realname LIKE CONCAT('%', #{userAdminForm.realname, jdbcType=VARCHAR},'%') </ if > </ where > ORDER BY A.id ASC </ select > </ mapper > |
4.工具方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package com.wanma.framework_web.helper; import cn.hutool.core.util.StrUtil; import org.springframework.transaction.interceptor.TransactionAspectSupport; /** * SQL助手类 */ public class DbHelper { /** * 事务回滚 * 参考:/2231.html * //配合注解: * //@Transactional(rollbackFor = Exception.class) */ public static void rollBack() { try { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } catch (Exception e) { e.printStackTrace(); } } /** * 转义Like字符串 * 参考:/2246.html */ public static String escapeLike(String likeValue) { String likeEscapeChr = "\\\\" ; if (StrUtil.isEmpty(likeValue)) { return likeValue; } return likeValue.replaceAll(likeEscapeChr, likeEscapeChr + likeEscapeChr) .replaceAll( "%" , likeEscapeChr + "%" ) .replaceAll( "_" , likeEscapeChr + "_" ); } } |