java中 ${} 和 #{} 有什么区别

目录
  • 前言
  • 1.功能不同
    • 1.1 ${} 的问题
  • 2.使用场景不同
  • 3.安全性不同
    • 3.1 使用 ${} 实现用户登录
    • 3.2 使用 #{} 实现用户登录
  • 总结

前言

${} 和 #{} 都是 MyBatis 中用来替换参数的,它们都可以将用户传递过来的参数,替换到 MyBatis 最终生成的 SQL 中,但它们区别却是很大的,接下来我们一起来看。

1.功能不同

${} 是将参数直接替换到 SQL 中比如以下代码:

<select id="getUserById" resultType="com.example.demo.model.UserInfo">
    select * from userinfo where id=${id}
</select>

最终生成的执行 SQL 如下: 

从上图可以看出,之前的参数 ${id} 被直接替换成具体的参数值 1 了。 而 #{} 则是使用占位符的方式,用预处理的方式来执行业务,我们将上面的案例改造为 #{} 的形式,实现代码如下:

<select id="getUserById" resultType="com.example.demo.model.UserInfo">
    select * from userinfo where id=#{id}
</select>

最终生成的 SQL 如下: 

1.1 ${} 的问题

当参数为数值类型时(在不考虑安全问题的前提下),${} 和 #{} 的执行效果都是一样的,然而当参数的类型为字符时,再使用 ${} 就有问题了,如下代码所示:

<select id="getUserByName" resultType="com.example.demo.model.UserInfo">
    select * from userinfo where name=${name}
</select>

以上程序执行时,生成的 SQL 语句如下: 

这样就会导致程序报错,因为传递的参数是字符类型的,而在 SQL 的语法中,如果是字符类型需要给值添加单引号,否则就会报错,而 ${} 是直接替换,不会自动添加单引号,所以执行就报错了。 而使用 #{} 采用的是占位符预执行的,所以不存在任何问题,它的实现代码如下:

<select id="getUserByName" resultType="com.example.demo.model.UserInfo">
    select * from userinfo where name=#{name}
</select>

以上程序最终生成的执行 SQL 如下: 

2.使用场景不同

虽然使用 #{} 的方式可以处理任意类型的参数,然而当传递的参数是一个 SQL 命令或 SQL 关键字时 #{} 就会出问题了。比如,当我们要根据价格从高到低(倒序)、或从低到高(正序)查询时,如下图所示: 

此时我们要传递的排序的关键字,desc 倒序(价格从高到低)或者是 asc 正序(价格从低到高),此时我们使用 ${} 的实现代码瑞安:

<select id="getAll" resultType="com.example.demo.model.Goods">
  select * from goods order by price ${sort}
</select>

以上代码生成的执行 SQL 和运行结果如下: 

 但是,如果将代码中的 ${} 改为 #{},那么程序执行就会报错,#{} 的实现代码如下:

<select id="getAll" resultType="com.example.demo.model.Goods">
  select * from goods order by price #{sort}
</select>

以上代码生成的执行 SQL 和运行结果如下: 

 从上述的执行结果我们可以看出:当传递的是普通参数时,需要使用 #{} 的方式,而当传递的是 SQL 命令或 SQL 关键字时,需要使用 ${} 来对 SQL 中的参数进行直接替换并执行。

3.安全性不同

${} 和 #{} 最主要的区别体现在安全方面,当使用 ${} 会出现安全问题,也就是 SQL 注入的问题,而使用 #{} 因为是预处理的,所以不会存在安全问题,我们通过下面的登录功能来观察一下二者的区别。

3.1 使用 ${} 实现用户登录

UserMapper.xml 中的实现代码如下:

<select id="login" resultType="com.example.demo.model.UserInfo">
  select * from userinfo where name='${name}' and password='${password}'
</select>

单元测试代码如下:

@Test
void login() {
    UserInfo userInfo = userMapper.login("java", "java");
    System.out.println(userInfo);
}

以上代码生成的执行 SQL 和运行结果如下: 

从结果可以看出,当我们传入了正确的用户名和密码时,能成功的查询到数据。但是,在我们使用 ${} 时,当我们在不知道正确密码的情况下,使用 SQL 注入语句也能用户的私人信息,SQL 注入的实现代码如下:

@Test
void login() {
    UserInfo userInfo = userMapper.login("java", "' or 1='1");
    System.out.println(userInfo);
}

以上代码生成的执行 SQL 和运行结果如下: 

从上述结果可以看出,当使用 ${} 时,在不知道正确密码的情况下也能得到用户的私人数据,这就像一个小偷在没有你们家钥匙的情况下,也能轻松的打开你们家大门一样,这是何其恐怖的事情。那使用 #{} 有没有安全问题呢?接下来我们来测试一下。

3.2 使用 #{} 实现用户登录

首先将 UserMapper.xml 中的代码改成以下内容:

<select id="login" resultType="com.example.demo.model.UserInfo">
  select * from userinfo where name=#{name} and password=#{password}
</select>

接着我们使用上面的 SQL 注入来测试登录功能:

@Test
void login() {
    UserInfo userInfo = userMapper.login("java", "' or 1='1");
    System.out.println(userInfo);
}

最终生成的 SQL 和执行结果如下: 

从上述代码可以看出,使用 SQL 注入是无法攻破 #{} 的“大门”的,所以可以放心使用。

总结

${} 和 #{} 都是 MyBatis 中用来替换参数的,它们二者的区别主要体现在:1、功能不同:${} 是直接替换,而 #{} 是预处理;2、使用场景不同:普通参数使用 #{},如果传递的是 SQL 命令或 SQL 关键字,需要使用 ${},但在使用前一定要做好安全验证;3、安全性不同:使用 ${} 存在安全问题,而 #{} 则不存在安全问题。

到此这篇关于java中 ${} 和 #{} 有什么区别的文章就介绍到这了,更多相关java中${} 和 #{} 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java字符串中${}或者{}等的占位符替换工具类

    正如标题所述,这是一个替换java字符串中${}或者{}等占位符的工具类,其处理性能比较令人满意.该类主要通过简单的改写myatis框架中的GenericTokenParser类得到.在日常开发过程中,可以将该类进行简单的改进或封装,就可以用在需要打印日志的场景中,现在张贴出来给有需要的人,使用方式参考main方法,不再赘述! public class Parser { /** * 将字符串text中由openToken和closeToken组成的占位符依次替换为args数组中的值 * @par

  • java中字符串参数化符号${}的解析

    前言 我们在很多地方都能看到代表参数意义的符号${},可能我们在写一些框架的时候,有时候也需要用到这个符号,但他们是如何精确解析的?或者说需要我们自已写的时候,如何写? 我们先来看以下的几个场景: 1.字符串"a${a}a" 2.字符串"a\${a}a" 3.字符串"a${a\}a" 4.字符串"a${a\}a}a" 5.字符串"a${a}a${" 6.字符串"a${a}a${a}" 以

  • 教你如何用Java替换Word中带有${}的内容

    一.概述 1.因为有些需求,需要把word文档里面的特定数据,设置成可变的:所以需要某种方式,把可变量用标签(如${变量名})替换,通过后端赋值此变量名,重新生成的Word就能根据后端设置的内容变化. 2.替换方法:准备一份word模板文档,如:word_mode.doc(或 word_mode.docx) 文件,把可变内容,用标签${变量名}替换(如图1姓名:${name}) 3.转成可读模板:全部设置完变量标签后,对此word文档进行另存为xml格式的文档(图2),保存后的文件名:word_

  • 详解Java中“==”与equals()的区别

    Java中"=="与equals()的区别 对于关系操作符"==",<Java编程思想>中是这样描述的:"关系操作符生成的是一个boolean结果,它们计算的是操作数的值之间的关系".这里的操作数的"值"值得我们注意.对于8种基本数据类型(boolean,byte,char,short,int,float,double,long),它们的变量直接存储的就是"值".所以,我们用"==&q

  • java equals和=,==的区别详细介绍

    Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boolean 他们之间的比较,应用双等号(==),比较的是他们的值. 2.复合数据类型(类) 当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false. JAVA当中所有的类都是继承于Object这个基类的,在Ob

  • Java集合和数组的区别

    集合和容器都是Java中的容器. 区别 数组特点:大小固定,只能存储相同数据类型的数据 集合特点:大小可动态扩展,可以存储各种类型的数据 转换 数组转换为集合: Arrays.asList(数组) 示例: int[] arr = {1,3,4,6,6}; Arrays.asList(arr); for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); } 集合转换为数组: 集合.toArray(); 示例: List list = ne

  • js的for in循环和java里foreach循环的区别分析

    本文实例分析了js的for in循环和java里foreach循环的区别.分享给大家供大家参考.具体分析如下: js里的for in循环定义如下: 复制代码 代码如下: for(var variable in obj) { ... } obj可以是一个普通的js对象或者一个数组.如果obj是js对象,那么variable在遍历中得到的是对象的属性的名字,而不是属性对应的值.如果obj是数组,那么variable在遍历中得到的是数组的下标. 遍历对象实验: 复制代码 代码如下: var v = {

  • Java字符流与字节流区别与用法分析

    本文实例讲述了Java字符流与字节流区别与用法.分享给大家供大家参考,具体如下: 字节流与字符流主要的区别是他们的的处理方式 流分类: 1.Java的字节流 InputStream是所有字节输入流的祖先,而OutputStream是所有字节输出流的祖先. 2.Java的字符流 Reader是所有读取字符串输入流的祖先,而writer是所有输出字符串的祖先. InputStream,OutputStream,Reader,writer都是抽象类.所以不能直接new 字节流是最基本的,所有的Inpu

  • java 抽象类与接口的区别总结

    java 抽象类与接口的区别总结 abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java强大的面向对象能力. abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于 abstract class和interface 选择显得比较随意. 其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的 理解

  • 详解 Java 中 equals 和 == 的区别

    详解 Java 中 equals 和 == 的区别 1 前言 在 Java 语言中,equals 和 == 都是用来检测两个字符串是否相等,返回值也都是布尔型(boolean),但是两者在内部比较的处理中却不尽相同,因此在需要检测两个字符串是否相等的时候,我们一定要特别的注意,选择适当的检测方式,防止造成不必要的 bug.从表面上来看,这种 bug 很像随机产生的间歇性错误. 2 区别 在需要检测两个字符串是否相等的时候,我们可以使用 equals 方法.对于表达式: s.equals(t) 如

  • 浅谈Java中replace与replaceAll区别

    看门见山 1.java中replace API: replace(char oldChar, char newChar):寓意为:返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的. replace(CharSequence target, CharSequence replacement):寓意为:使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串. replaceAll(String regex, String replacem

  • 详解Java中Thread 和Runnable区别

    Thread 和Runnable 关系 Thread类是接口Runnable的一个实现类. public class Thread implements Runnable 源码分析 Thread Threa类运行的时候调用start()方法,源代码如下: 调用start()方法,实际运行的是start0方法,方法声明如下: private native void start0() native表明这个方法是个原生函数,即这个函数是用C/C++实现的,被编译成DLL,由Java调用. native

  • 浅谈java 中equals和==的区别

    本文实例为大家分享了java 中equals和==的区别的具体代码,供大家参考,具体内容如下 java9举例代码: String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = new String("abc"); 当: str1 == str2    输出:true 当:str1.equals(str2); 输

随机推荐