目录
- 一、前言
- 二、 `#{}` 和`${} `的使用方法和区别
- 2.1 `#{}`使用方法
- 2.2 `${}`使用方法
- 2.3`#{}` 和 `${}` 的主要区别
- 2.4使用建议
- 三、总结
一、前言
在 MyBatis 中,#{}
和 ${}
都用于在 SQL 语句中绑定参数,但它们在具体实现和安全性方面有所不同。理解它们的区别对于编写高效且安全的 MyBatis 映射文件非常重要。这篇文章主要讲一下他们的使用和区别。
数据库数据信息:
二、 #{}
和${}
的使用方法和区别
2.1 #{}
使用方法
在 MyBatis 中,#{}
用于绑定参数值,将其直接插入到 SQL 语句中。它不进行任何类型转换,适用于简单类型的参数绑定,如字符串、整数等。
示例:
UserInfoMapper.xml 文件
<select id="selectById" resultType="java.lang.Integer">select * from user_info where id=#{id}</select>
UserInfoMapperXML接口
List<UserInfo> selectById(Integer id);
测试类
@Testvoid selectById() {userInfoMapperXML.selectById(3).stream().forEach(x-> System.out.println(x));}
运行结果:
作用解析:
#{id}
会将参数值直接替换到 SQL 语句中。- 我们输⼊的参数并没有在后面拼接,id的值是使用 ? 进行占位. 这种SQL 我们称之为"预编译SQL"。
- 适用于不需要类型转换的简单参数。
2.2 ${}
使用方法
我们把上面的UserInfoMapper.xml 文件
改一下,把#{}
改成${}
再观察打印日志。
UserInfoMapper.xml 文件:
<select id="selectById" resultType="com.sliqvers.model.UserInfo">select * from user_info where id=${id}</select>
观察运行结果日志:
可以看到, 这次的参数是直接拼接在SQL语句中了.
2.3#{}
和 ${}
的主要区别
再举一个例子来对比一下#{}
和${}
的区别。
#{}
UserInfoMapperXML接口
List<UserInfo> selectByusername(String username);
UserInfoMapper.xml 文件
<select id="selectByusername" resultType="com.sliqvers.model.UserInfo">select * from user_info where username=#{username}</select>
测试类
@Testvoid selectByusername() {userInfoMapperXML.selectByusername("Bob").stream().forEach(x-> System.out.println(x));}
运行结果:
我们再来看:
${}
我们把
UserInfoMapper.xml 文件
稍微改一下,其他的不变。
<select id="selectByusername" resultType="com.sliqvers.model.UserInfo">select * from user_info where username=${username}</select>
我们再来看运行结果:
可以看到这个报错了,因为username是字符串类型,这个Bob少了两个引号。
我们把这个引号加上去:
<select id="selectByusername" resultType="com.sliqvers.model.UserInfo">select * from user_info where username='${username}'</select>
运行结果:
可以看到问题得以解决。
综上可以得出他们的区别.
#{} 使用的是预编译SQL, 通过 ? 占位的方式, 提前对SQL进行编译, 然后把参数填充到SQL语句中. #{} 会根据参数类型, 自动拼接引号 ‘’ .
${} 会直接进行字符替换, ⼀起对SQL进行编译. 如果参数为字符串, 需要加上引号 ‘’ .
特性 | #{} | ${} |
---|---|---|
参数处理 | 将参数值直接替换为字符串,不进行类型转换 | 对参数值进行 JDBC 类型转换 |
安全性 | 旧版本可能存在 SQL 注入风险,新版本默认安全 | 旧版本可能存在 SQL 注入风险,新版本默认安全 |
使用场景 | 适用于简单类型的参数绑定 | 适用于需要类型转换的参数绑定或动态 SQL 场景 |
示例 | select * from users where username = #{username}; | select * from users where birthday = ${birthday}; |
提出问题:
从上面的例子中, 可以得出结论:${}
会有SQL注入的风险, 所以我们尽量使用#{}
完成查询。
既然如此, 是不是 ${} 就没有存在的必要性了呢?
当然不是⼀些场景,
#{}
不能完成, 比如 排序功能, 表名, 字段名作为参数时, 这些情况需要使用${}
。
${}的使用场景:
设计一个排序查询:
UserInfoMapperXML.java 文件:
List<UserInfo> selectBysort(String sort);
UserInfoMapper.xml文件
<select id="selectBysort" resultType="com.sliqvers.model.UserInfo">select * from user_info order by age #{sort}</select>
测试类:
@Testvoid selectBysort() {userInfoMapperXML.selectBysort("asc").stream().forEach(x-> System.out.println(x));}
运行结果:
可以看到报错了。因为此处 sort 参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 ‘’ 的。
我们把#{sort}
换成${}
然后看一下运行结果:
<select id="selectBysort" resultType="com.sliqvers.model.UserInfo">select * from user_info order by age ${sort}</select>
运行成功了。
模糊查询虽然
${}
可以完成, 但因为存在SQL注入的问题,所以通常使用mysql内置函数concat来完成。
设计一个like
查询:
UserInfoMapperXML.java 文件:
List<UserInfo> selectBylike(String like);
UserInfoMapper.xml文件
<select id="selectBylike" resultType="com.sliqvers.model.UserInfo">select * from user_info where username like `${like}%`</select>
测试类:
@Testvoid selectBysort() {userInfoMapperXML.selectBysort("asc").stream().forEach(x-> System.out.println(x));}
使用#{}
会报错,使用${}
可以运行但是不安全,存在SQL注入的问题,所以使用MySQL的内置函数concat()来进行。
UserInfoMapper.xml文件
<select id="selectBylike" resultType="com.sliqvers.model.UserInfo">select * from user_info where username like concat('${like}%')</select>
运行结果:
2.4使用建议
何时使用 #{}
:
- 当需要绑定的参数是简单类型,如字符串、整数。
- 当不需要对参数进行类型转换时。
何时使用 ${}
- 当需要对参数进行类型转换时,如日期、Blob 类型。
- 当需要动态生成 SQL 语句时,例如根据条件添加 WHERE 子句。
安全注意事项:
- 无论使用
#{}
还是${}
,都要确保参数值已经过适当的过滤和验证,以防止 SQL 注入。- 依赖 MyBatis 的预编译语句和类型转换功能,增强安全性。
三、总结
#{}
和${}
都用于 MyBatis 的参数绑定,但#{}
不涉及类型转换,而${}
会进行类型转换。#{}
适用于简单类型的参数绑定,${}
适用于复杂类型或需要类型转换的场景。- 在使用时,重点关注参数的安全性,确保参数已经过适当的验证和过滤。
通过合理选择#{}
和${}
,可以编写出高效、安全的 MyBatis 映射文件,提升开发效率。