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 则刚好相反

这就引出了预编译的概念:

  • 如果使用PreparedStatement 对象,那么在执行第3步时,你既然已经传入了sql,则相当于这条sql会被数据库编译(数据库对sql语句的编译也是相当复杂的),所以在第4步执行的时候就不用再传入sql了,因为数据库已经知道你要执行的sql了,你只需要传入参数即可;
  • 如果使用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拼接在一起时,那就得用${}传参,相当于拼接好了之后丢给

数据库去解析整个语句;

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 ${}和#{}取值区别内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MyBatis中#{}和${}的区别详解

    最近在用mybatis,之前用过ibatis,总体来说差不多,不过还是遇到了不少问题,再次记录下. 先给大家介绍下MyBatis中#{}和${}的区别,具体介绍如下: 1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号.如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id". 2. $将传入的数据直接显示生成在sql中.

  • MyBatis 中 ${}和 #{}的正确使用方法(千万不要乱用)

    1.#{}是预编译处理,MyBatis在处理#{ }时,它会将sql中的#{ }替换为?,然后调用PreparedStatement的set方法来赋值,传入字符串后,会在值两边加上单引号,如上面的值 "4,44,514"就会变成" '4,44,514' ": 2.是字符串替换,在处理是字符串替换,MyBatis在处理时,它会将sql中的{}是字符串替换,在处理{ }是字符串替换, MyBatis在处理{ }时,它会将sql中的是字符串替换,在处理是字符串替换,MyB

  • 详解mybatis #{}和${}的区别、传参、基本语法

    1 #{}和${}的区别.及注入问题 (1) 区别: 首先清楚一点,动态 SQL 是 mybatis 的强大特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析,#{} 和 ${} 在预编译中的处理是不一样的: 例如:select * from t_user where userName = #{name}; #{}预编译:用一个占位符 ? 代替参数:select * from t_user where userName = ? #{}预编

  • Mybatis中#{}与${}的区别详解

    前言 在开发中使用Mybatis经常使用到#{}与${},依旧有很多开发者对二者的使用不是很清晰,正所谓好记性不如烂笔头,特此总结一下. 在mybatis中动态 sql 是其主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在执行操作之前 mybatis 会对其进行动态解析.mybatis 提供了两种支持动态 sql 的语法:#{} 以及 $ { }, 其最大的区别则是前者方式能够很大程度防止sql注入(安全),后者方式无法防止Sql注入 .什么??不懂什么是Sql注入?额...

  • Mybatis中#{}和${}传参的区别及#和$的区别小结

    最近在用mybatis,之前用过ibatis,总体来说差不多,不过还是遇到了不少问题,再次记录下, 比如说用#{},和 ${}传参的区别, 使用#传入参数是,sql语句解析是会加上"",比如 select * from table where name = #{name} ,传入的name为小李,那么最后打印出来的就是 select * from table where name = '小李',就是会当成字符串来解析,这样相比于$的好处是比较明显对的吧,#{}传参能防止sql注入,如果

  • MyBatis中#{}占位符与${}拼接符的用法说明

    1.关于#{}占位符 先来看以下的示例,该示例是MyBatis中的SQL映射配置文件(Mapper配置文件),在该配置中使用了#{}占位符. <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mappe

  • 详解Mybatis中的 ${} 和 #{}区别与用法

    Mybatis 的Mapper.xml语句中parameterType向SQL语句传参有两种方式:#{}和${} 我们经常使用的是#{},一般解说是因为这种方式可以防止SQL注入,简单的说#{}这种方式SQL语句是经过预编译的,它是把#{}中间的参数转义成字符串,举个例子: select * from student where student_name = #{name} 预编译后,会动态解析成一个参数标记符?: select * from student where student_name

  • Mybatis之#{}与${}的区别使用详解

    1.两种取值方式的差异 mapper.xml映射文件 <select id="selectEmployeeByCondition2" resultMap="empResultMap" databaseId="mysql"> select * from t_emp WHERE emp_id=${id} and emp_name=#{name} </select> java查询代码 params 为 id=1 ,name=&q

  • mybatis中${}和#{}取值的区别分析

    mybatis作为一个轻量级的ORM框架,应用广泛,其上手使用也比较简单:一个成熟的框架,必然有精巧的设计,值得学习. 在使用mybatis框架时,在sql语句中获取传入的参数有如下两种方式: ${paramName} #{paramName} 那如何理解这两种传参方式呢?如下带你走近背后的奥义. 先来回顾下原生Jdbc查询: public static void main(String[] args) throws Exception { // sql语句 String sql = "sele

  • GET和post取值限制区别分析

    其实没有太大的区别 都是传送资料到服务器上的 比较重要的一个区别是 使用Get方式的话,你会在URL中看到类似这样的内容 "?item=##&quantity=##" 相比较Post,Get显得不怎么安全 不过炎人为Get或者Post都无所谓-- Get的话,让用户也知道提交表格的时候发送了哪些信息,不是不错的吗? 如果你用百度博客就知道,你在编辑器中写的内容都是用Post方式传送的 因为你在Address URL中看不到你博文中写的内容 还有在美观上,Post还是占上风 如果

  • 微信小程序实现同一页面取值的方法分析

    本文实例讲述了微信小程序实现同一页面取值的方法.分享给大家供大家参考,具体如下: 1.js里单个的值在wxml里取值方法:js里将该值定义为全局变量,在wxml里采用 {{ }}即可获取. 实例: js里得值: data{ schoolName:"清华大学" } wxml里获取: <view class="texts">{{schoolName}}</view> 2.js里数组或是集合在wxml里的取值方法:js里将该集合或数组定义为全局变量

  • 微信小程序页面间传值与页面取值操作实例分析

    本文实例讲述了微信小程序页面间传值与页面取值操作.分享给大家供大家参考,具体如下: 微信小程序页面间传值 1.js页面间的传值:url: ' 路径 ? 值='+ 获取该页面取值, 实例1: pageSkip :function(){ wx.navigateTo({ url: '/page/login/facte?shopsName='+this.data.shopsName, }) }, 实例2: pageSkip :function(){ wx.navigateTo({ url: '/page

  • C++中引用&与取地址&的区别分析

    C++中的引用&与取址&是很多初学者经常容易出错的地方,今天本文就对此加以分析总结,供大家参考之用. 具体而言,一个是用来传值的 一个是用来获取首地址的 &(引用)==>出现在变量声明语句中位于变量左边时,表示声明的是引用.      例如: int &rf; // 声明一个int型的引用rf &(取地址运算符)==>在给变量赋初值时出现在等号右边或在执行语句中作为一元运算符出现时表示取对象的地址. 在C++中,既有引用又有取地址,好多人对引用和取地址不

  • js取值中form.all和不加all的区别介绍

    在js里取值,可以用form.xx.value,也可以用form.all.xx.value,那么他们有什么区别呢? 原先想当然的以为加all是取form里的所有xx(如果有多个name相同的元素),返回的是数组,后来在网上搜索了一下,发现不是这个意思, all代表的是form里的所有元素,就是说form.all可以访问<form></form>标签包含的任意元素,包括div.table等,而form.xx只能访问表单元素,像input.select等. 测试如下: 复制代码 代码如

  • php 数组的合并、拆分、区别取值函数集

    合并数组有三个函数: 1.array_combine() 携带两个参数数组,参数数组一的值作新数组的键,参数数组二的值作新数组的值.很简单. 例子: 复制代码 代码如下: <?php $a = array('green', 'red', 'yellow'); $b = array('avocado', 'apple', 'banana'); $c = array_combine($a, $b); print_r($c); ?> 上例将输出: 复制代码 代码如下: Array ( [green]

  • 浅谈jsp EL表达式取值过程、page和pagecontext的区别

    1.EL表达式解析过程 JSP中,我们经常会写为${obj.name}字样,但你有没有想过,它的取值过程是什么,属性值从哪取得? ${obj}相当于 request.getAttribute("obj"),这句话严格来说不严谨,依次的请求范围是page.request.session.application 也就是说,如果在page.getAttribute()找不到,再去request.getAttribute,如果request找不到,再去session里找,session里找不到

  • 详解Mybatis 传递参数类型为List的取值问题

    问题描述: 参数传递为List时: 当传递一个 List 实例或者数组作为参数对象传给 Mybatis.此时,Mybatis 会自动将它包装在一个 Map 中,用名称在作为键.List 实例将会以"list" 作为键,而数组实例将会以"array"作为键.所以,当我们传递的是一个List集合时,mybatis会自动把我们的list集合包装成以list为Key值的map. DAO 层: List<User> selectUserByIDs( List ID

  • 关于mybatis if else if 条件判断SQL片段表达式取值和拼接问题

    前言 最近在开发项目的时候涉及到复杂的动态条件查询,但是mybaits本身不支持if elseif类似的判断但是我们可以间接通过 chose when otherwise 去实现其中choose为一个整体 when是if otherwise是else 快速使用 以前我们进行条件判断时候使用if标签进行判断,条件并列存在 <if test="seat_no != null and seat_no != '' "> AND seat_no = #{seat_no} </i

随机推荐