Mybatis - #{} 和 ${} 的区别是什么?
一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 63w+ 字,讲解图 2808+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2200+ 小伙伴加入学习 ,欢迎点击围观
简单介绍一下
首先,两者都是占位符,它们区别如下:
#{}
是参数占位符,可以预编译处理,能够防止 SQL 注入,提高系统安全性;${}
是变量占位符,直接用于字符串替换,一般在使用 Sharding-JDBC 中间件进行分库分表时,动态指定表名时可能会用到。
深入解释
关于 #{}
#{}
是 SQL 的参数占位符,Mybatis 会将 SQL 中的 #{}
替换为 ?
号,在 SQL 执行前会使用 PreparedStatement 的参数设置方法,按序给 SQL 的 ?
号占位符设置参数值,如 ps.setInt(0, parameterValue)
。
所以,#{}
是预编译处理,它可以有效防止 SQL 注入,提高系统安全性,推荐使用。
关于 ${}
${}
常见于相关配置文件中,常被用于 XML 标签属性值以及 SQL 内部,用来做字符串替换。例如将 ${driver}
会被静态替换为 com.mysql.jdbc.Driver
:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${driver}"/>
<!-- 基本属性 url、user、password -->
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="1"/>
<property name="minIdle" value="1"/>
<property name="maxActive" value="20"/>
另外,${}
也可以对传递进来的参数原样拼接在 SQL 中。代码如下:
<select id="findUser" parameterType="Integer" resultType="User">
SELECT * FROM t_user
WHERE user_id = ${userId}
</select>
注意:生产环境下,不推荐这么做。会带来 SQL 注入的风险。
什么时候会用到 ${} 呢?应用场景?
在使用类似 Sharding-JDBC 等分库分表中间件时会用到,比如说,有张用户表 t_user
, 考虑到未来用户数据的增长,以及查询的效率(使每张表数据量保持在 500w 之内),设计时根据 user_id 分了 8 张表, 如下所示:
这时,在代码逻辑层就需要根据 user_id 动态指定表名,也就是说,对表名路由时,需要对 user_id % 8
处理后,才能得到记录具体落到哪张表:
String tableName = "t_user_" + (userId % 8);
算出表名后,再动态设置表名:
<select id="findUser" resultType="User">
SELECT * FROM ${tableName}
</select>
补充
#{}
和 ${}
的取值方式都异常方便。例如:#{item.name}
,框架在进行解析时,根据规则,使用反射可以很方便地从参数对象中,获取 item
对象的 name
属性值,相当于 param.getItem().getName()
。