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