• 【MyBatis】#{ } 和 ${ } 的区别


    #{ } 和 ${ } 的区别

    #{ }:预编译处理。
    ${ }:字符直接替换。

    预编译处理是指:MyBatis 在处理 #{ } 时,会将 SQL 中的 #{…} 替换为 ? 号,使⽤ PreparedStatement 的 set ⽅法来赋值。
    直接替换:是MyBatis 在处理 ${ } 时,就是把 ${ } 直接替换成变量的值。

    1. 定义不同: #{ } 是预处理, ${ } 是直接替换
    2. 安全性不同: #{ } 没有安全问题, ${ } 有 SQL 注入问题
    3. 使用场景不同: #{ } 适用于所有类型的参数匹配, 但是 ${ } 只使用于数值类型和关键字

    #{ } 使用场景

    • 使用 #{ }
        <insert id="add">
            insert into userinfo(username,password,photo,state)
            values(#{username},#{password},#{photo},1)
        </insert>
    
    • 1
    • 2
    • 3
    • 4
    ==>  Preparing: insert into userinfo(username,password,photo,state) values(?,?,?,1)
    ==> Parameters: zhaoliu(String), 666(String), img.png(String)
    <==    Updates: 1
    
    • 1
    • 2
    • 3

    先将 参数的位置替换为 ? 然后再把对应类型的参数填入进去.

    • 使用 ${ }
        <insert id="add">
            insert into userinfo(username,password,photo,state)
            values(${username},${password},${photo},1)
        </insert>
    
    • 1
    • 2
    • 3
    • 4
    ==>  Preparing:insert into userinfo(username,password,photo,state) values(zhaoliu,666,img.png,1)
    ==> Parameters: 
    
    • 1
    • 2

    直接替换上了,所以 Parameters 为空, 替换时也不加 引号, 所以会插入失败

    like 查询使用 #{ } 报错

        <select id="findUserByName" resultType="com.example.springboot3.model.User">
            select * from userinfo where username like '%#{username}%';
        </select>
    
    • 1
    • 2
    • 3

    相当于:

    select * from userinfo where username like '%'username'%' 
    
    • 1

    所以代码报错

    这个是不能直接使⽤ ${ },可以考虑使⽤ mysql 的内置函数 concat() 来处理

    concat('a', 'b', 'c')  ==> 'abc'
    
    • 1

    实现代码如下:

        <select id="findUserByName" resultType="com.example.springboot3.model.User">
            select * from userinfo where username like concat('%', #{username}, '%');
        </select>
    
    • 1
    • 2
    • 3
    ==>  Preparing: select * from userinfo where username like concat('%', ?, '%');
    ==> Parameters: zhaoliu(String)
    <==    Columns: id, username, password, photo, createtime, updatetime, state
    <==        Row: 7, zhaoliu, 666, img.png, 2023-10-09 11:29:00.0, 2023-10-09 11:29:00.0, 1
    <==      Total: 1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ${ } 的使用场景

    关键字的替换, 比如排序

        <select id="getAll" resultType="com.example.springboot3.model.User">
            select * from userinfo order by id ${rule}
        </select>
    
    • 1
    • 2
    • 3
    ==>  Preparing: select * from userinfo order by id desc
    ==> Parameters: 
    
    • 1
    • 2

    使⽤ ${ } 可以实现排序查询,⽽使⽤ #{ } 就不能实现排序查询了,当使⽤ #{ } 查 询时,如果传递的值为 String 则会加单引号,就会导致 sql 错误。

    所以:当传递的是一个 SQL 关键字时, 只能使用 ${ }

    SQL 注入问题

    假如我们真的使用了 ${ }, 那么因为我们知道 ${ } 是直接替换, 不会 加引号, 那么我们就会自己加上引号, 比如下面:

        <select id="isLogin" resultType="com.example.springboot3.model.User">
            select * from userinfo where username='${name}' and password='${pwd}'
        </select>
    
    • 1
    • 2
    • 3

    但是, 这样会引入一个很大的安全问题: SQL 注入问题

    sql 注入代码关键部分:

    "' or 1='1"
    
    • 1

    调用接口进行测试:

            List<User> list = userMapper.isLogin("afeagdee445", "' or 1 = '1");
            if (list.size() > 0) {
                System.out.println("登录成功");
            }
    
    • 1
    • 2
    • 3
    • 4

    查看结果:

    ==>  Preparing: select * from userinfo where username='afeagdee445' and password='' or 1 = '1'
    ==> Parameters: 
    <==    Columns: id, username, password, photo, createtime, updatetime, state
    <==        Row: 1, admin, admin, , 2021-12-06 17:10:48.0, 2021-12-06 17:10:48.0, 1
    <==        Row: 2, mysql, mysql, img.png, 2023-10-08 15:09:07.0, 2023-10-08 15:09:07.0, 1
    <==        Row: 3, mysql, mysql, img.png, 2023-10-08 15:24:24.0, 2023-10-08 15:24:24.0, 1
    <==        Row: 4, mysql, mysql, img.png, 2023-10-08 15:24:50.0, 2023-10-08 15:24:50.0, 1
    <==        Row: 5, mysql, mysql, img.png, 2023-10-08 15:25:59.0, 2023-10-08 15:25:59.0, 1
    <==        Row: 6, mysql, mysql, img.png, 2023-10-08 15:26:40.0, 2023-10-08 15:26:40.0, 1
    <==        Row: 7, zhaoliu, 666, img.png, 2023-10-09 11:29:00.0, 2023-10-09 11:29:00.0, 1
    <==      Total: 7
    登录成功
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    登录成功了.
    也就是说, 我们什么都不知道, 只要使用 SQL 注入就能得登录成功

    由打印的 SQL 日志 我们就能看出来原因了

    select * from userinfo where username='afeagdee445' and password='' or 1 = '1'
    
    • 1

    拼接出来的这个 SQL 的判断条件一定为 true, 因为最后是一个 or 1 = ‘1’, 这一定是 true 的.

    但如果使用 #{ } 就不会有问题了

    ==>  Preparing: select * from userinfo where username=? and password=?
    ==> Parameters: afeagdee445(String), ' or 1 = '1(String)
    <==      Total: 0
    
    • 1
    • 2
    • 3

    它这个等价于:

    select * from userinfo where username='afeagdee445' and password="' or 1 = '1"
    
    • 1

    这里面单引号和双引号并不会匹配上

    结论:⽤于查询的字段,尽量使⽤ #{} 预查询的⽅式。
    当不得不使用 ${ } 时,在业务代码中一定要对传递的值进行校验.

    好啦! 以上就是对 #{ } 和 ${ } 区别 的讲解,希望能帮到你 !
    评论区欢迎指正 !

  • 相关阅读:
    Excel必备!6种快速插入√或x标记的方法揭秘
    jsp数据交互(一)
    剑指Offer51数组中的逆序对(相关话题:线段树)
    使用Tipas结合内网穿透在Ubuntu上搭建高效问题解答平台网站
    php如何查找地图距离
    可观测性数据收集集大成者 Vector 介绍
    【sfu】network线程和主线程
    elementui tree组件自定义内容,实现移入label显示操作按钮
    JavaWeb 学习笔记 7:Filter
    Android Compose 权限请求
  • 原文地址:https://blog.csdn.net/m0_61832361/article/details/133697553