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来完成

相关推荐
sdaxue.com14 分钟前
帝国CMS:如何去掉帝国CMS登录界面的认证码登录
数据库·github·网站·帝国cms·认证码
o(╥﹏╥)1 小时前
linux(ubuntu )卡死怎么强制重启
linux·数据库·ubuntu·系统安全
阿里嘎多学长1 小时前
docker怎么部署高斯数据库
运维·数据库·docker·容器
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
Sunyanhui11 小时前
牛客网 SQL36查找后排序
数据库·sql·mysql
老王笔记2 小时前
MHA binlog server
数据库·mysql
lovelin+v175030409662 小时前
安全性升级:API接口在零信任架构下的安全防护策略
大数据·数据库·人工智能·爬虫·数据分析
DT辰白3 小时前
基于Redis的网关鉴权方案与性能优化
数据库·redis·缓存
2401_871213303 小时前
mysql高阶语句
数据库·mysql