请简述MyBatis中 #{} 和 ${} 之间的区别。


​ 首先 #{}${} 都可以用来读取调用Mapper接口时传递给方法的形参值,形参可以是基本类型、引用类型(对象),不管是读取基本类型还是对象中的属性,都可以用 #{name}${name} 直接获取到。

该两者有以下区别:

一、执行过程不同

#{} : 对读取到的参数先使用?来占位,然后去预编译SQL,最后再将?替换为形参值。

${} : 直接替换读取到的形参值,没有预编译的过程。

举例1

有一个名为userinfo的用户信息表,表结构如下:
在这里插入图片描述该表中有一条记录

在这里插入图片描述

核心代码:

UserMapper接口

在这里插入图片描述

UserMapper.xml实现

在这里插入图片描述
在这里插入图片描述

这里的SQL语句唯一不同的是#和$符号

UserMapperTest测试类

在这里插入图片描述

现在我通过 整型参数 id 查询表中的 username 和 password,看一下分别使用 #{}${} 的SQL执行结果。

#{}的SQL打印结果:

在这里插入图片描述

${}的SQL打印结果:
在这里插入图片描述

举例2

例1是读取整型参数的情况,如果我们读取的是字符串类型,SQL语句就不单是#和$占位符一字之差了。

现在我们通过字符串参数username来进行查询。

核心代码:

UserMapper接口

在这里插入图片描述

UserMapper.xml实现

在这里插入图片描述

在这里插入图片描述

这里我先让两个语句仅是有一个占位符的区别。

UserMapperTest测试类

在这里插入图片描述

#{}的SQL打印结果:

在这里插入图片描述

${}的SQL打印结果:

在这里插入图片描述

为什么使用 ${} 读取字符串参数时会报错?我们把这里的SQL语句放到命令行客户端中再尝试一次。

在这里插入图片描述

可以看到是一样的错误结果。归根结底是因为${}会将读取到的参数值直接进行替换,而当我们使用字符串值做查询条件时,需要用双引号或单引号将其包裹起来,不然就会出错。

这里用加了引号的语句放到命令行客户端中跑一下,

在这里插入图片描述

可以看到查询到了数据,说明字符串值做查询条件必须要用引号包裹,那么我们将UserMapper.xml中的SQL语句修改一下,加上引号。

在这里插入图片描述

再运行程序,就看到了查询结果了。

在这里插入图片描述

为什么 ${} 需要手动添加引号,而 #{} 确不用?这是因为 #{} 对于字符串参数会帮我们自动添加引号。

小结:

#{}${} 的使用,对于读取整型参数无差别,当读取字符串参数时,使用 ${} 需要手动添加引号,而#{}不用。

二、SQL注入问题

#{} 是先预编译SQL,然后再替换占位符,不会出现通过SQL注入改变其SQL语句结构的问题;

${} 是直接替换,会出现SQL注入的问题。

举例

我们通过查询 username 和 password 字段来模拟一下最简单的登录过程,这里先看一个正常登录的情况。

核心代码:

UserMapper接口

在这里插入图片描述

UserMapper.xml实现

在这里插入图片描述

在这里插入图片描述

UserMapperTest测试类

在这里插入图片描述

#{}的SQL打印结果:

在这里插入图片描述

${}的SQL打印结果:

在这里插入图片描述

通过正常传参输入的用户名和密码,两种方式貌似都没问题,接下来我们来看一看通过SQL注入来实现非法登录的情况。

我们将测试代码中的密码参数值改成 ’ or 1 = '1

#{}的SQL打印结果:

在这里插入图片描述

使用 #{} 当密码错误时,并不会查询到数据,因为先通过预编译然后替换占位符,SQL语句的结构在预编译的时候已经固定了,这里替换成的参数值并不会影响其结构,所以不会有SQL注入问题。

${}的SQL打印结果:
在这里插入图片描述

因为 ${} 是直接替换且没有预编译的过程,传递过来的密码参数会存在修改SQL结构的情况,有SQL注入问题。

小结:

#{}没有SQL注入问题,${}存在SQL注入问题,在开发中千万要避免使用${}。

三、可以使用${}的情况

如果当我们传递的参数是SQL关键字时,直接使用#{}会报错,因为#{}会给字符串参数加上引号。可以考虑使用${},但也可以通过其他特殊手段来实现。

举例

现在有一个文章表articleinfo,表结构如下

在这里插入图片描述

表中有如下几条数据

在这里插入图片描述

现在我想查询出这几条数据,并按阅读量rcount字段的指定排序顺序来返回。

核心代码:

ArticleMapper接口

在这里插入图片描述

ArticleMapper.xml实现

在这里插入图片描述

在这里插入图片描述

ArticleMapperTest测试类

在这里插入图片描述

#{}的SQL打印结果:

在这里插入图片描述

因为#{}替换成字符串参数时会自动添加引号,所以会报错误信息。

${}的SQL打印结果:

而${}可以解决这个问题

在这里插入图片描述

当传参是SQL关键字时,我们可以考虑使用${}来读取,但是一定要对参数值进行合法性校验,且参数值必须是可以穷举的,像这里的 asc/desc 就只有两种情况,因为${}存在SQL注入问题,所以不推荐使用,当传递的是SQL关键字,我们可以用其他方式例如表单中的true或false来决定执行asc还是desc。

扩展

如果写过原生JDBC代码的小伙伴应该知道两个接口:StatementPreparedStatement,我们使用PreparedStatement来进行SQL预编译然后替换占位符,而在MyBatis框架中,${}就对应着Statement,#{}对应着PreparedStatement,框架会帮我们完成拼串的过程然后执行SQL。

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MyBatis的一级缓存是指在SqlSession对象的生命周期内,会对执行过的SQL语句进行缓存,避免多次执行同一条SQL语句而浪费时间。一级缓存是一个内部Map,保存着查询结果集合以及 mapped statement 文件的部分属性。当用户执行相同的查询求时,MyBatis会先检查一级缓存是否有可用的结果集,如果有,则直接返回缓存的结果,否则再执行SQL语句并将查询结果保存至一级缓存,以备下一轮重复调用时使用。需要注意的是,当SqlSession commit或close时,一级缓存也会随之清空。 ### 回答2: MyBatis是一款流行的Java持久层框架,它使用一级缓存来提高性能。一级缓存是指MyBatis在默认情况下在SQL会话级别对数据库查询结果进行缓存的机制。 具体来说,当执行一条SQL语句时,MyBatis会将查询结果存储在一级缓存。下次相同的SQL语句被执行时,MyBatis会首先检查一级缓存是否已经有了对应的结果。如果有,MyBatis会直接从缓存获取结果,而不再访问数据库,以提高响应速度。 一级缓存的有效范围是SQL会话级别,也就是在同一个会话,如果执行了多条相同的SQL语句,那么只有第一次执行会触发数据库查询,后续的查询都直接从缓存获取结果。而在不同的会话,一级缓存是不共享的,每个会话都维护自己的一级缓存。 除了自动缓存查询结果,MyBatis还提供了手动清除缓存的方法,开发人员可以通过调用`clearCache`方法来清空一级缓存。 一级缓存的机制可以显著提高查询性能,尤其是在高并发的情况下。但需要注意的是,由于一级缓存是在会话级别的,所以如果在同一个会话有更新或者删除操作,那么对应的缓存也会被清空,以保持数据的一致性。 ### 回答3: MyBatis的一级缓存是指在同一个SqlSession对于相同查询语句的结果会被缓存下来,以提高查询的性能和减轻数据库负载。 MyBatis的一级缓存机制是通过一个HashMap来实现的。当执行一条查询语句时,MyBatis会先去缓存查找是否已经存在相同的查询结果,如果存在则直接返回缓存的结果,不再访问数据库。如果缓存不存在相同的查询结果,MyBatis会执行查询操作,并将查询结果放入缓存。 缓存的生命周期与SqlSession的生命周期一致,当SqlSession关闭时,缓存也会被清空。 MyBatis的一级缓存是默认开启的,但是也可以通过SqlSession的clearCache方法手动清空缓存,或者通过设置<setting>标签的flushCache属性为true来在执行增删改操作后自动清空缓存。 一级缓存存在一些限制和注意事项: 1. 缓存的查找是基于Sql语句和参数完全匹配的,如果两次查询的参数不同,即使查询结果相同也不会命缓存。 2. 一级缓存是在同一个SqlSession共享的,不同的SqlSession之间的缓存是独立的。 3. 若在同一个SqlSession执行了增、删、改操作,会导致缓存失效,下次查询会重新访问数据库。 4. 当缓存占用内存较大时,可以考虑调整缓存的大小或者关闭缓存。 总结起来,一级缓存通过在同一个SqlSession缓存相同查询语句的结果,可以在减少数据库访问,提高查询性能和降低数据库负载方面起到一定的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值