Mybatis - #{} 和 ${} 的区别是什么?

更新时间: 2019-07-10 20:31:53   作者: 异常教程网

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 54w+ 字,讲解图 2476+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 1900+ 小伙伴加入学习 ,欢迎点击围观

简单介绍一下

首先,两者都是占位符,它们区别如下:

  • #{} 是参数占位符,可以预编译处理,能够防止 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()