目录
- 一、前言
- 二、 `#{}` 和`${} `的使用方法和区别
- 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 映射文件,提升开发效率。

