目录
MyBatis 参数赋值有两种方式:#{} 和 ${}
一. #{} 和${} 使用
1 对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"。
${}:
@Select("select username, `password`, age, gender, phone from userinfo where
id= ${id} ")
UserInfo queryById(Integer id);
这次的参数是直接拼接在SQL语句中。
2 对String类型的参数
#{}:
@Select("select username, `password`, age, gender, phone from userinfo where
username= #{name} ")
UserInfo queryByName(String name);
${}:
@Select("select username, `password`, age, gender, phone from userinfo where
username= '${name}' ")
UserInfo queryByName(String name);
字符串作为参数时, 需要添加引号
' '
, 使用 ${}
不会拼接引号
' '
, 如果不加就会导致程序报错。
注意到:#{} 使用的是预编译SQL, 通过 ? 占位的方式, 提前对SQL进行编译, 然后把参数填充到SQL语句中。 #{} 会根据参数类型, 自动拼接引号 ' ' 。${} 会直接进行字符替换, 一起对SQL进行编译。如果参数为字符串, 需要手动加上引号 ' ' 。参数为数字类型时,也可以加上,查询结果不变,但是可能会导致索引失效,性能下降。
二、#{} 和${} 区别
#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别。
当发送一条SQL语句给服务器后, 大致流程如下:
- 解析语法和语义, 校验SQL语句是否正确
- 优化SQL语句, 制定执行计划
- 执行并返回结果
一条 SQL如果走上述流程处理,称之为 Immediate Statements(即时 SQL)
1.性能更好
绝大多数情况下, 某一条 SQL 语句可能会被反复调用执行, 或者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同, update 的 set 子句值不同, insert 的 values 值不同)。如果每次都需要经过上面的语法解析, SQL优化、SQL编译等,则效率就明显不行。
预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同), 省去了解析优化等过程, 以此来提高效率。
2.SQL注入
SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。sql 注入代码:
' or 1 = ' 1
SQL注入举例:
@Select("select username, `password`, age, gender, phone from userinfo where
username= '${name}' ")
List<UserInfo> queryByName(String name);
@Test
void queryByName() {
List<UserInfo> userInfos = userInfoMapper.queryByName(" ' or 1='1 ");
System.out.println(userInfos);
}
可以看出,查询的数据并不是想要的数据。${} 可能会产生SQL注入的问题,所以用于查询的字段,尽量使用 #{} 预查询的方式。
SQL注入是一种非常常见的数据库攻击手段, SQL注入漏洞也是网络世界中最普遍的漏洞之一。 如果发生在用户登录的场景中, 密码输入为 ' or 1='1 , 就可能完成登录。
${} 会有SQL注入的风险, 所以尽量使用#{}完成查询。那么,
是不是 ${} 就没有存在的必要性了呢?
下面来看一下
${} 的使用场景:
1. 排序功能
Mapper实现:
@Select("select id, username, age, gender, phone, delete_flag, create_time,
update_time " +
"from userinfo order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);
使用
${sort} 可以实现排序查询。而使用
#{sort}
查询时会自动加引号, 会导致 sql 错误。
那么使用${}仍然存在SQL注入,需要控制参数,只设置为desc或者asc即可。
除此之外, 还有字段和表名作为参数时, 也只能使用
${}。但也需要考虑SQL注入。
2. like 查询
${}存在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);
总结
#{} 和${} 的区别:
1. $符号存在SQL注入问题;
2. $是即时SQL(参数直接拼接),#是预编译SQL(参数通过占位的方式);
3. 像排序功能,like查询等只能使用$,但是得解决SQL注入的问题。