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

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

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

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

简单介绍一下

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

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