不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2022-06-03 21:08:40  修改时间:2024-05-03 20:34:18  分类:Java框架/系统  编辑

方式一

在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等技术统一处理。

小总结

推荐使用第三种方式进行模糊查询

 

 

参考:

MySQL like 和 escape

MyBatis中Like 的三种使用方式

 

 




工作中的参考代码:

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 + "_");
    }
}