Mybatis中 #{} 和 ${} 的区别

MyBatis 参数赋值有两种方式 一种是 #{}一种是${}
这两种有什么区别呢?

查看Interger类型的参数
使用**#{}**

 @Select("select username, password, age, gender, phone from userinfo where id = #{id}")
    UserInfo queryById(Integer id);

观察打印的日志:
在这里插入图片描述
这里会显示我们输出的sql语句为

select username,password,age,gender,phone from userinfo where id = ?

我们在输出的参数并没有在后面拼接上 id是使用?进行占位 我们把这种sql称之为预编译sql

预编译sql:
是一种将 SQL 语句进行预处理的技术。在实际开发中,对数据库的基本操作通常是增删改查(CRUD),每次执行 SQL 都需要经过词法和语义解析、优化 SQL、制定执行计划等过程,这些操作比较耗时。为了减少编译次数,提高执行效率,就有了预编译的过程
预编译可以将 SQL 语句变成一个函数,在需要的时候传参进行即可使用。这样就能达到一次编译,多次运行的效果。通过预编译,可以避免重复解析和优化相同语法树的时间,提升了 SQL 执行的效率

让后我们将#{}改成${}

@Select("select username, password, age, gender, phone from userinfo where id = ${id}")
    UserInfo queryById(Integer id);

这时我们查看日志:
在这里插入图片描述

这时使用${}参数就直接拼接到sql语句中了

String类型的参数

  @Select("select id, username, password,age,gender,phone,delete_flag,create_time,update_time " +
            "from userinfo where username = #{name}")
    UserInfo queryByName(String name);

在这里插入图片描述
换成${}

  @Select("select id, username, password,age,gender,phone,delete_flag,create_time,update_time " +
            "from userinfo where username = ${name}")
    UserInfo queryByName(String name);

这时我们发现参数依然是直接拼接在SQL语句中

在这里插入图片描述
但是正常情况下sql对于字符串需要拼接’'而${}并没有导致程序报错需要修改以下代码

 @Select("select id, username, password,age,gender,phone,delete_flag,create_time,update_time " +
            "from userinfo where username = '${name}'")
    UserInfo queryByName(String name);

继续运行得到正常结果
在这里插入图片描述
从上面可以看出:

排序:
我们根据id做升序排序
使用${}

   @Select("select * from userinfo order by id #{sort}")
    List<UserInfo> selectUserBySort(String sort);

我们会发现这里的#{}会对关键字加上’’
而传入字符串必须加上’'"
所有我们在排序查询时不能使用#{}
在这里插入图片描述

在这里插入图片描述
而使用${}则没有这个问题

#{}使用的时预编译sql通过?占位的方式提前队sql进行编译,然后把参数填充到SQL语句中. #{} 会根据参数类型, ⾃动拼接引号 ‘’
${} 会直接进⾏字符替换, ⼀起对SQL进⾏编译. 如果参数为字符串, 需要加上引号 ‘’ (参数为数字类型时, 也可以加上, 查询结果不变, 但是可能会导致索引失效, 性能下降)

sql注入问题

 @Select("select id, username, password,age,gender,phone,delete_flag,create_time,update_time " +
            "from userinfo where username = '${name}' ")
    List<UserInfo> queryByName2(String name);

正常访问时:

@Test
    void queryByName2() {
        List<UserInfo> userInfoList =userInfoMapper.queryByName2("admin");
        log.info(userInfoList.toString());
    }

结果是正常的
在这里插入图片描述

当我们使用${name},然后sql代码注入:

'or 1='1

@Test
    void queryByName2() {
        List<UserInfo> userInfoList =userInfoMapper.queryByName2("' or 1='1");
        log.info(userInfoList.toString());
    }

这时我们查看结果就会发现

在这里插入图片描述
这里查询数据并没有出现自己预期的,

这是因为在 SQL 中,当多个条件用“OR”连接时,只要其中一个条件成立,整个表达式就为真。 在这个被注入后的语句中,username = ‘’ 这个条件很可能不成立,但是 1=1 这个条件是恒成立的。而“OR”的逻辑就是只要有一边成立就行,所以整个条件表达式就总是成立的。当条件总是成立时,数据库就会认为所有的行都满足这个条件,因此就会返回表中的所有用户记录

这就是sql注入漏洞是⼀种⾮常常⻅的数据库攻击⼿段
如果发⽣在⽤⼾登录的场景中, 密码输⼊为 ’ or 1='1 , 就可能完成登录(不是⼀定会发⽣的场景,需要看登录代码如何写)
所以为了防止被sql注入攻击我们会使用 #{}

模糊查询(like)

like使用#{}报错
在这里插入图片描述

在 MyBatis 中,#{} 用于预编译参数,会将参数值进行转义处理,以防止 SQL 注入。而 like 操作符在 SQL 中用于模糊匹配,需要与通配符 % 一起使用。
然而,直接在 #{} 中使用通配符 % 可能会导致报错,因为 #{} 会将参数值视为字符串,并在其周围添加引号。这样,like ‘%#{value}%’ 实际上会被解析为 like ‘%‘value’%’,其中的 #{value} 被视为一个普通的字符串,而不是通配符。
为了解决这个问题,可以采用字符串拼接的方式来实现 like 查询。例如,可以将查询条件构建为 like ‘%’ + #{value} + ‘%’,这样在预编译时,#{value} 会被正确地替换为参数值,而 % 则作为通配符被正确解析

我们应该把#{}改成 $ {}可以查出来但是 $ {}存在sql注入问题不能直接用mysql内置函数concat()处理
例如

   @Select("select id, username, age, gender, phone, delete_flag, create_time," +
            "update_time"+" from userinfo where username like concat('%',#{key},'%') ")
    List<UserInfo> queryAllUserBylike(String key);

在这里插入图片描述
总结:
#{} 和 ${} 区别
#{}:预编译处理, ${}:字符直接替换
#{} 可以防⽌SQL注⼊, $ {}存在SQL注⼊的⻛险, 查询语句中, 可以使⽤ #{} ,推荐使⽤ #{}
但是⼀些场景, #{} 不能完成, ⽐如 排序功能, 表名, 字段名作为参数时, 这些情况需要使⽤ $ {}
模糊查询虽然${}可以完成, 但因为存在SQL注⼊的问题,所以通常使⽤mysql内置函数concat来完成

  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值