浅析mybatis中${}和#{}取值区别
mybatis作为一个轻量级的ORM框架,应用广泛,其上手使用也比较简单;一个成熟的框架,必然有精巧的设计,值得学习。
在使用mybatis框架时,在sql语句中获取传入的参数有如下两种方式:
${paramName}#{paramName}
那如何理解这两种传参方式呢?如下带你走近背后的奥义。
先来回顾下原生Jdbc查询:
public static void main(String[] args) throws Exception {
// sql语句
String sql = "select id,name from customer limit 2";
// 1.加载驱动, 此处使用的mysql驱动包是8.0版本, 若为5.0+版本, 请修改以下类路径
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获取数据库连接
String url = "jdbc:mysql://localhost:3306/work?useSSL=false&useUnicode=true" +
"&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true" +
"&useLegacyDatetimeCode=false&serverTimezone=UTC";
Connection conn = DriverManager.getConnection(url,"root", "123456");
// 3、获得可以执行sql语句的对象
Statement st = conn.createStatement();
// 4、使用对象去执行SQL语句
ResultSet rs = st.executeQuery(sql);
// 5、处理sql语句返回的结果集
while(rs.next()){
// 获得一行数据
Integer id = rs.getInt("id");
String name = rs.getString("name");
System.out.println("sql查询: id = " + id + " , name = " + name);
}
// 6、释放资源
rs.close();
st.close();
conn.close();
}
控制台打印:
sql查询: id = 1 , name = 李白
sql查询: id = 2 , name = 杜甫
了解Jdbc的人会知道,其中第3、4步两条语句也可以换成如下两条:
// 3.创建 PreparedStatement 对象去执行sql
PreparedStatement preparedStatement = conn.prepareStatement(sql);
// 4.执行sql语句
ResultSet rs = preparedStatement.executeQuery();
我们来比较下区别:
- 创建 PreparedStatement 对象时就把sql语句传入,在执行语句时就不用传入sql了;而 Statement 则刚好相反
这就引出了预编译的概念:
1.如果使用PreparedStatement 对象,那么在执行第3步时,你既然已经传入了sql,则相当于这条sql会被数据库编译(数据库对sql语句的编译也是相当复杂的),所以在第4步执行的时候就不用再传入sql了,因为数据库已经知道你要执行的sql了,你只需要传入参数即可;
2.如果使用Statement对象,那容易理解,数据库就没有提前去解析你的sql,因为你创建对象时都没有传入;当执行sql时,数据库再编译与执行。
看到这里,可能也仅仅只记住了一个预先编译sql了,一个没有预先编译,并没有了解到对于实际开发中的区别,以下将会举例说明。
那是否PreparedStatement对象这种方式就一定比Statement对象方式好?
没有那么绝对的事,大家要理解:
PreparedStatement对象的好处是,sql已经提前编译好,剩下的工作就是传入参数即可,编译好的sql可以复用,传入不同的参数,则数据库就将相应的参数填入编译好的sql。而Statement对象就是每次都要传入sql,丢给数据库去编译再执行;但是创建PreparedStatement对象的开销是比Statement对象大的。 回归到日常开发中,以上的区别我们压根也不用在意,事实上,百分之九十的场景我们使用的是
PreparedStatement对象的方式,可能平时没有感知到,因为这是框架已经封装了。再者,当系统出现性能问题时,也绝对不会是因为这两个对象的原因。
以上简单回顾了下Jdbc中PreparedStatement与Statement对象;
可以预料,mybatis中${} 与 #{} 这两种取值方式就是相当于对应着PreparedStatement和Statement对象的区别了。
#{} 传参,代表sql已经预编译好了,你传入的参数真的就仅仅是参数!${} 传参,随便你传,传完了之后我再统一编译
那具体在使用中有什么不同呢?理解如下两种场景:
1.看如下service和sql语句
@Override
public List<Map<String, Object>> listUser() {
String param = " and name = '李白'";
return indexMapper.listUser(param);
}
<select id="listUser" resultType="map">
select * from customer
where 1 = 1 #{param}
</select>
以上代码能正常查询吗?
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have
an error in your SQL syntax; check the manual that corresponds to your MySQL
server version for the right syntax to use near '' and name = \'李白\''
不能!会报sql语句规则错误,之前说了,#{} 取值代表sql已经编译好了,你传入的仅仅是参数
对应上面示例:
sql是指:select * from customer where 1 = 1
参数是指:and name = '李白'
此时sql可以正确运行,但是带上传入的参数就不行了,要理解你传入的真的仅仅是参数,不要和前面的sql混了。
但是这明显不对,因为想表达的其实是这样:
sql是指:select * from customer where 1 = 1 and name = ?
参数是指:'李白'
此时把参数替换进占位符是可以正常运行整个语句的。
所以此时应该用 ${param},因为用${}就没有提前编译好哪些是属于sql。
此示例表明:当你传入的参数不仅仅是参数,其实是一小段sql,想和原sql拼接在一起时,那就得用${}传参,相当于拼接好了之后丢给
Statement对象去解析整个语句;
sql中的问号代表参数占位符,这也是PreparedStatement对象特点之一,会将你传入的参数一一替换进占位符
反之如下:
@Override
public List<Map<String, Object>> listUser() {
String param = "李白";
return indexMapper.listUser(param);
}
<select id="listUser" resultType="map">
select * from customer
where 1 = 1 and name = #{param}
</select>
这种情况使用 #{} 就是对的了,因为传入的参数仅仅就是参数,替换进sql语句中即可。
2.对参数类型的影响
@Override
public List<Map<String, Object>> listUser() {
String param = "李白";
return indexMapper.listUser(param);
}
<select id="listUser" resultType="map">
select * from customer
where 1 = 1 and name = ${param}
</select>
以上代码能执行成功吗?
按理说,传入的仅仅是参数,不管是否预编译都应该能执行,但是实际还是会报错。
这是执行时打印出的sql语句:
select * from customer where 1 = 1 and name = 李白
显然,问题就在于参数没有加单引号,name字段是字符串类型,传入的也是字符串,偏偏mybatis转换之后没有加单引号。
所以当传入字符串类型参数时,应该用 #{} 取值,此时会自动加上单引号。
再看下面这种语句:
@Override
public List<Map<String, Object>> listUser() {
String param = "name";
return indexMapper.listUser(param);
}
<select id="listUser" resultType="map">
select * from customer
where 1 = 1
order by ${param} desc
</select>
此时传入的参数是要排序的字段名称,之前说了,如果采用#{} 取值,则实际是会自动加上单引号的,但是order by后面的排序字段需要单引号吗?
不需要,所以这种情况只能使用 ${} 取值。
你可能会发现此处用 #{} 取值也不会报错,那是因为mysql支持这种写法,但是查询的结果并不对。
日常开发中,只要能理解上述两种情形,那么就能正确使用 ${} 和 #{},由于这两种方式取值原理的区别,也容易明白 #{} 这种方式是可以防止sql注入的。
若有错误,望指出交流。
浅析mybatis中${}和#{}取值区别的更多相关文章
- mybatis学习$与#号取值区别
1,多个参数传递用map或实体封装后再传给myBatis, mybatis学习$与#号取值区别 #{} 1.加了单引号, 2.#号写是可以防止sql注入,比较安全 select * from use ...
- MyBatis 3.0_[tp-24-25]_映射文件_参数处理_#与$取值区别_#{}更丰富的用法
笔记要点出错分析与总结 /**================Mybatis参数值的获取:#和$符号的区别=============== * #{}:可以获得map中的值或者pojo对象属性的值; * ...
- mybatis中 ${}和#取值小记(Parameter index out of range)
mybatis mapperxml文件中有两种取值法.${}和#{} $的是原样,#的是取值并转成指定?#{ele1,jdbcType=VARCHAR} 有个坑, 错误的写法 <if test= ...
- Java SSM框架之MyBatis3(七)MyBatis之参数取值
在mybatis中,参数取值方式有两种:#{ } 和 ${ } 一.#{ } select * from student where name=#{name} 编译后执行的sql语句: select ...
- 8、SpringBoot+Mybatis整合------参数取值方式
前言: 我们知道,在mybatis中,参数取值方式有两种: #{ } 和 ${ } 下面,我们来探讨下#{ }与${ }不同. 一.#{ } 例: select * from student wher ...
- mybatis中#{}与${}取值的区别
1. 首先对于一个接口 Employee getEmpByIdAndName(@Param("id") Integer id,@Param("empName") ...
- .html() 与.text() 获取值、取值 区别
1.html代码<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <t ...
- mybatis映射文件参数处理 #{}取值与${}取值的区别
#{}:是以预编译的映射,将参数设置到sql语句中,和jdbc的preraredStatement一样,使用占位符,防止sql注入. ${}:取出的值会直接拼装在sql中,会有安全问题. 大多数情况下 ...
- mybatis中两种取值方式?谈谈Spring框架理解?
1.mybatis中两种取值方式? 回答:Mybatis中取值方式有几种?各自区别是什么? Mybatis取值方式就是说在Mapper文件中获取service传过来的值的方法,总共有两种方式,通过 $ ...
随机推荐
- 刷了无数大厂Android研发岗面试题,其实考的无非是这 3 点能力
前言 发现一个有趣的现象,似乎程序员们对面试题总是抱有热情,多看几道面试题,自己的面试能力就可以提高一点. 作为一个研发工程师,看过很多公司的面试题,也参与过很多公司的面试,发现大厂的面试题更加具有代 ...
- 线程礼让_yield
线程礼让_yield 礼让线程,让当前正在执行的线程暂停,但不阻塞 将线程从运行状态转为就绪状态 让cpu重新调度,礼让不一定成功!看CPU心情 测试案例: package multithreadin ...
- Java EE-下载安装eclipse并设置环境变量的步骤
1.下载eclipse: 官网:https://www.eclipse.org/downloads/ (1)点击链接后显示如图 (2)点击Download Packages 下载安装包,不要点击&qu ...
- 如何请求一个需要登陆才能访问的接口(基于cookie)---apipost
在后台在开发.调试接口时,常常会遇到需要登陆才能请求的接口. 比如:获取登陆用户的收藏列表,此时,我们就需要模拟登陆状态进行接口调试了.如图: 今天,我们讲解利用ApiPost的环境变量,解决这种需要 ...
- 那些shellcode免杀总结
首发先知: https://xz.aliyun.com/t/7170 自己还是想把一些shellcode免杀的技巧通过白话文.傻瓜式的文章把技巧讲清楚.希望更多和我一样web狗也能动手做到免杀的实现. ...
- luogu P2473 奖励关
奖励关 看到数据范围,想到状压,那问题就是如何设计方程 设\(dp[i][j]\)表示在第\(i\)轮的时候,状态为\(j\)时的最优策略所拿的分值,\(j\)的二进制下为1的位置,表示选了这个宝物, ...
- Anaconda安装和使用
Anaconda anaconda (开源的Python包管理器) 编辑 讨论 上传视频 Anaconda指的是一个开源的Python发行版本,其包含了conda.Python等180多个科学包及其依 ...
- VMware + LInux + Xshell 连接环境设置
一.安装好Centos后,打开设置: 网络连接四中模式桥接模式.NAT模式.仅主机和自定义模式: 1).桥接模式:更公司的局域网连接,等于另一台连接进公司的新电脑 2).NAT模式:与虚拟机使用的计算 ...
- C# 多线程刷新UI
2.利用委托调用--最常见的办法(仅WinForm有效) using System; using System.Threading; using System.Windows.Forms; nam ...
- 【转】new和malloc的区别
1. 申请的内存所在位置 new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存. 自由存储区是C++基于new操作符的一个抽象概念,凡是通过n ...