方式一
在MyBatis中的第一种写法:
<!--有sql注入问题--> <select id="findUserByLikeName1" parameterType="java.lang.String" resultMap="user"> select * from t_user where name like '%${name}%' </select>
这种会有sql注入的问题,需要明白在 MyBatis中 $ 和 # 使用的区别。这种写法也不能加jdbcType=VARCHAR,否则也会报错。
做了个简单的测试:
@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());// 查出了全部数据 }
注意:排序的字段也容易出现这个问题,在使用的时候也一定要注意。
order by ${orderBy}
第一种方式在实际开发过程中千万要注意,不要写成这样了。
方式二
在MyBatis中的第二种写法:
<!--直接在代码中拼接%,不存在sql注入--> <select id="findUserByLikeName2" parameterType="java.lang.String" resultMap="user"> select * from t_user where name like #{name,jdbcType=VARCHAR} </select>
在代码中加上%
。
@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中的第三种写法:
<!--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>
测试:
@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如下:
select * from t_user where name like concat(’%’,’%’,’%’)
查出来全部的数据,并不是只包含了%
的数据,如果查询_
也是一样的。
那这种情况肯定是不满足查询需求的,则需要调整。
①在代码中进行转义
@Test public void findUserByLikeName3(){ String name = "%"; name = name.replaceAll("_", "\\\\_"); name = name.replaceAll("%", "\\\\%"); List<User> test = userMapper.findUserByLikeName3(name); System.out.println(test.size()); }
②使用ESCAPE
<select id="findUserByLikeName4" parameterType="java.lang.String" resultMap="user"> select * from t_user where name like concat('%',#{name,jdbcType=VARCHAR},'%') ESCAPE '/' </select>
测试:
@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.控制器
@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
public interface UserMapper extends BaseMapper<User> { Page<UserPO> getUserPageList(UserAdminForm userAdminForm, Page<UserPO> page); }
3.UserMapper.xml
<?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.工具方法
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 + "_"); } }