【MyBatis】MyBatis操作数据库(二):动态SQL、#{}与${}的区别

简介: 【MyBatis】MyBatis操作数据库(二):动态SQL、#{}与${}的区别

一、 动态SQL

什么是动态SQL,简单来说就是为了满足我们的条件可以将不同是SQL语句进行拼接,来使用.

1.1 <if>标签

在我们编程SQL语句中,时常有些参数不知道是否传参,在以往的编程中我们是使用if语句进行判读,在这里也同样如此,只是换成了<if>标签进行判断。

<if>标签的代码如下:

<if test="gender != null">
gender,
</if>

使用<if>标签将我们要判断的内容包裹起来即可。

1.2 <trim>标签

在我们判断的时候,有时会有多余的标点符号或是我们想要在sql语句前添加一下其它关键词,这时候就该<trim>标签派上用场了。

<trim>标签一共有四个属性,记住了属性是添加在<trim 属性>中的,记住添加的位置.

有以下四个属性:

  • prefix:表⽰整个语句块,以prefix的值作为前缀
  • suffix:表⽰整个语句块,以suffix的值作为后缀
  • prefixOverrides:表⽰整个语句块要去除掉的前缀
  • suffixOverrides:表⽰整个语句块要去除掉的后缀

举例代码如下:

<trim prefix="(" suffix=")" suffixOverrides=",">
  <if test="username !=null">
    username,
  </if>
  
  <if test="password !=null">
    `password`,
  </if>
  
  <if test="age != null">
    age,
  </if>
  
  <if test="gender != null">
    gender,
  </if>
  
  <if test="phone != null">
    phone,
  </if>
  
</trim>

可以看到动态SQL是可以一起进行拼接使用的,在举例的该段代码中用到了,prefix用来在前缀添加、suffix用来后缀添加、suffixOverrides用来删除后缀标点,相信举了这个例子另外的的一个predixOverrides也会用了吧!

1.3 <where>标签

在SQL语句中,where关键字一般是用来进行条件判断的,在动态SQL中也是如此,只是我们给它换成<where>的标签形式会更加合理方便

代码如下:

<where>
  <if test="age != null">
    and age = #{age}
  </if>
  
  <if test="gender != null">
    and gender = #{gender}
  </if>
  
  <if test="deleteFlag != null">
    and delete_flag = #{deleteFlag}
  </if>
  
</where>

在动态SQL中使用<where>标签能够在⼦元素有内容的情况下才插⼊where⼦句,⽽且会⾃动去除⼦句的开头的AND或OR

1.4 <set>标签

<set>标签标签一般用在修改数据库的SQL语句中,可以使⽤标签来指定动态内容.

代码如下:

<set>
  <if test="username != null">
    username = #{username},
  </if>
  
  <if test="deleteFlag != null">
    delete_flag = #{deleteFlag},
  </if>
</set>

<set> :动态的在SQL语句中插⼊set关键字,并会删掉额外的逗号.(⽤于update语句中)

1.5 <foreach>标签

对集合进⾏遍历时可以使⽤该标签。标签有如下属性:

• collection:绑定⽅法参数中的集合,如List,Set,Map或数组对象

• item:遍历时的每⼀个对象

•open:语句块开头的字符串

• close:语句块结束的字符串

• separator:每次遍历之间间隔的字符串

代码如下:

<foreach collection="ids" item="id" separator="," open="(" close=")">
  #{id}
</foreach>

使用<foreach>标签就跟使用常见代码中的foreach用法一样,都是用来遍历数组等结构里的每一个元素.

1.6 <include>标签

在xml映射⽂件中配置的SQL,有时可能会存在很多重复的⽚段,此时就会存在很多冗余的代码,而<include>就是将重复的代码提取出来,在后面使用该重复代码中直接拿出来就行了,这里与resultMapper倒是有点相似.

我们可以对重复的代码⽚段进⾏抽取,将其通过 <sql> 标签封装到⼀个SQL⽚段,然后再通过

<include> 标签进⾏引⽤。

<sql> :定义可重⽤的SQL⽚段

<include>:通过属性refid,指定包含的SQL⽚段

展示定义重复的代码-<sql>标签如下:

<sql id="allColumn">
  id, username, age, gender, phone, 
  delete_flag, create_time,update_time
</sql>

使用<include>标签调用已经定义好的代码块如下:

<include refid="allColumn"></include>

通过<sql>标签定义的属性名进行调用.


二、 #{}与${}的区别

MyBatis参数赋值有两种⽅式,咱们前⾯使⽤了 #{} 进⾏赋值,接下来我们看下⼆者的区别

#{}和${}都是在MyBatis操作数据库时进行取值的操作,且两者有以下三点区别,可能平时#{}是用的最多的,但既然${}存在便有其存在的价值和用处。

#{}和${}的区别:

  1. #{}是预编译sql,${}是即时sql
  2. #{}防止了sql注入,而${}可能被sql注入
  3. #{}的缓存性能比${}强大
  4. ${}特殊用处:数据库进行排序…等操作时

2.1 #{}是预编译sql,${}是即时sql

#{}是预编译sql,简单来说就是在程序编译时,用?来作占位符,然后根据传入的参数类型进行填充,如果传入的参数是String类型,则在填充时会自动给数据添加双引号"",来表示该字符串。

sql代码如下:

select username, `password`, age, gender, phone from userinfo where id= ?

我们输⼊的参数并没有在后⾯拼接,id的值是使⽤ ? 进⾏占位.这种SQL我们称之为"预编译SQL"

然而${}是即时SQL简单来说就是没有占位符,不管你输入的数据是啥,它直接给你填充到参数的位置上去,即使是字符串类型也没有特殊处理,这时为了保证SQL正确,我们要自己手动添加""。

下面是使用${}时的SQL代码:

@Select("select username, `password`, age, gender, phone from userinfo where
id= ${id} ")
UserInfo queryById(Integer id);

代码运行的结果是:

可以看到,我们输入的参数直接添加到了SQL语句中了,这就是即时SQL-${}

小总结:

从上⾯例⼦可以看出:

#{} 使⽤的是预编译SQL,通过 ? 占位的⽅式,提前对SQL进⾏编译,然后把参数填充到SQL语句中.

#{} 会根据参数类型,⾃动拼接引号"‘’ . ${} 会直接进⾏字符替换,⼀起对SQL进⾏编译.如果参数为字符串,需要加上引号 ‘’ .

2.2 SQL注入

首先来了解一下什么是SQL注入,SQL注入是由于前后端的漏洞而导致的Bug,黑客攻击人员可以通过前端的登录页面将数据输入到数据库,以达到修改甚至是删除数据库的目的。

通过上面学习我们可知,#{}是预编译SQL,而${}是即时SQL,当黑客在前端输入的数据是字符型sql“delete 数据库 ”,如果我们后端是用预编译SQL-#{}来取值的话,系统会看它是字符类型而添加"",进而导致SQL语句错误无法执行。

而使用即时SQL的话,是直接进行SQL拼接,会把delete database数据库直接拼接上去不带引号,这时数据库执行就会成功,把该公司的数据库删除掉。

小总结:

#{}会根据数据类型进行判断是否添加双引号""

KaTeX parse error: Expected 'EOF', got '#' at position 21: …拼接上去,使数据库被黑客攻击 #̲{}能够防止SQL注入,而{}会被SQL注入攻击

2.3 #{}性能高于${}

绝⼤多数情况下,某⼀条SQL语句可能会被反复调⽤执⾏,或者每次执⾏的时候只有个别的值不同(⽐如select的where⼦句值不同,update的set⼦句值不同,insert的values值不同).

如果每次都需要经过上⾯的语法解析,SQL优化、SQL编译等,则效率就明显不⾏了.

预编译SQL,编译⼀次之后会将编译后的SQL语句缓存起来,后⾯再次执⾏这条语句时,不会再次编译(只是输⼊的参数不同),省去了解析优化等过程,以此来提⾼效率

2.4 ${}用于排序功能

${}会有SQL注⼊的⻛险,所以我们尽量使⽤#{}完成查询,所谓它的存在就有一定的道理,如下在排序时就要用到即时SQL了。

@Select("select id, username, age, gender, phone, delete_flag, create_time,
update_time " +
"from userinfo order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);

使⽤${sort} 可以实现排序查询,⽽使⽤#{sort} 就不能实现排序查询了.

注意:此处sort参数为String类型,但是SQL语句中排序规则是不需要加引号 ‘’ 的,所以此时的${sort} 也不加引号,而使用#{}则会自动添加双引号"",就无法实现排序的功能了。

相关文章
|
15天前
|
SQL 分布式计算 DataWorks
DataWorks操作报错合集之SQL错误[0A000],通常是什么造成的
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
14 1
|
14天前
|
缓存 Java 数据库连接
我们后端程序员不是操作MyBatis的CRUD Boy
大家好,我是南哥。一个对Java程序员进阶成长颇有研究的人,今天我们接着新的一篇Java进阶指南。为啥都戏称后端是CRUD Boy?难道就因为天天怼着数据库CRUD吗?要我说,是这个岗位的位置要的就是你CRUD,你不得不CRUD。哪有公司天天能给你搭建高并发、高可用、大数据框架的活呢,一条业务线总要成长吧,慢慢成熟了就要装修工来缝缝补补、美化美化,也就是CRUD的活。不能妄自菲薄CRUD Boy,我们是后端工程师。今天来指南下操作数据库之MyBatis框架。
我们后端程序员不是操作MyBatis的CRUD Boy
|
2天前
|
SQL Java 数据库连接
mybatis动态SQL常用语法总结
MyBatis 使用 OGNL 表达式语言处理动态SQL,如 `if` 标签进行条件判断,`choose`、`when`、`otherwise` 实现多条件选择,`where`、`set` 管理SQL关键字,`trim` 提供通用修剪功能,`foreach` 遍历集合数据。`sql` 和 `include` 用于代码重用,`selectKey` 处理插入后的返回值。参数传递支持匿名、具名、列表、Map、Java Bean和JSON方式。注意SQL转义及使用合适的jdbcType映射Java类型。
20 7
|
5天前
|
SQL 缓存 Java
使用MyBatis优化Java持久层操作
使用MyBatis优化Java持久层操作
|
10天前
|
SQL Java 数据库连接
深入理解SQL中的LEFT JOIN操作
深入理解SQL中的LEFT JOIN操作
|
17天前
|
SQL Java 数据库连接
深入探索MyBatis Dynamic SQL:发展、原理与应用
深入探索MyBatis Dynamic SQL:发展、原理与应用
|
1天前
|
存储 消息中间件 NoSQL
Redis 和 数据库区别
【7月更文挑战第12天】
5 0
|
5天前
|
SQL 缓存 Java
使用MyBatis优化Java持久层操作
使用MyBatis优化Java持久层操作
|
8天前
|
数据库
MybatisPlus3---常用注解,驼峰转下滑线作为表明 cteateTime 数据表中的 cteate_time,@TableField,与数据库字段冲突要使用转义字符“`order`“,is
MybatisPlus3---常用注解,驼峰转下滑线作为表明 cteateTime 数据表中的 cteate_time,@TableField,与数据库字段冲突要使用转义字符“`order`“,is
|
8天前
|
XML 关系型数据库 MySQL
支付系统----微信支付19---集成MyBatis-plus,数据库驱动对应的依赖版本设置问题,5没版本没有cj这个依赖,mysql驱动默认的是版本8,这里是一个父类,数据库都有,写个父类,继承就行
支付系统----微信支付19---集成MyBatis-plus,数据库驱动对应的依赖版本设置问题,5没版本没有cj这个依赖,mysql驱动默认的是版本8,这里是一个父类,数据库都有,写个父类,继承就行