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

MyBatis 参数赋值有两种方式 一种是 #{}一种是${}

这两种有什么区别呢?

查看Interger类型的参数

使用**#{}**

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

观察打印的日志:

这里会显示我们输出的sql语句为

sql 复制代码
select username,password,age,gender,phone from userinfo where id = ?

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

预编译sql:

是一种将 SQL 语句进行预处理的技术。在实际开发中,对数据库的基本操作通常是增删改查(CRUD),每次执行 SQL 都需要经过词法和语义解析、优化 SQL、制定执行计划等过程,这些操作比较耗时。为了减少编译次数,提高执行效率,就有了预编译的过程

预编译可以将 SQL 语句变成一个函数,在需要的时候传参进行即可使用。这样就能达到一次编译,多次运行的效果。通过预编译,可以避免重复解析和优化相同语法树的时间,提升了 SQL 执行的效率

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

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

这时我们查看日志:

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

String类型的参数

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

换成${}

java 复制代码
  @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对于字符串需要拼接''而${}并没有导致程序报错需要修改以下代码

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

继续运行得到正常结果

从上面可以看出:

排序:

我们根据id做升序排序

使用${}

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

我们会发现这里的#{}会对关键字加上''

而传入字符串必须加上''"

所有我们在排序查询时不能使用#{}

而使用${}则没有这个问题

#{}使用的时预编译sql通过?占位的方式提前队sql进行编译,然后把参数填充到SQL语句中. #{} 会根据参数类型, ⾃动拼接引号 ''

${} 会直接进⾏字符替换, ⼀起对SQL进⾏编译. 如果参数为字符串, 需要加上引号 '' (参数为数字类型时, 也可以加上, 查询结果不变, 但是可能会导致索引失效, 性能下降)

sql注入问题

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

正常访问时:

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

结果是正常的

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

'or 1='1

java 复制代码
@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()处理

例如

java 复制代码
   @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来完成

相关推荐
阿华的代码王国1 小时前
MySQL ------- 索引(B树B+树)
数据库·mysql
Hello.Reader2 小时前
StarRocks实时分析数据库的基础与应用
大数据·数据库
执键行天涯2 小时前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
yanglamei19622 小时前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
工作中的程序员2 小时前
ES 索引或索引模板
大数据·数据库·elasticsearch
严格格2 小时前
三范式,面试重点
数据库·面试·职场和发展
工业甲酰苯胺2 小时前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
微刻时光2 小时前
Redis集群知识及实战
数据库·redis·笔记·学习·程序人生·缓存
单字叶3 小时前
MySQL数据库
数据库·mysql
mqiqe3 小时前
PostgreSQL 基础操作
数据库·postgresql·oracle