当前位置: 首页 > article >正文

Mybatis——04

本篇主要讲述#{}和${},数据库连接池。

目录

一、#{}和${}的使用

1.当参数为Integer类型时

2.当参数为String类型时

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

1.#{}性能更高

2.#{}更安全(防止SQL注入)

3.${}和#{}使用场景

排序功能

like查询

4.总结

三、数据库连接池

1.介绍

2.常见的数据库连接池


 

一、#{}和${}的使用

Mybatis的参数赋值方式有两种,分别是#{}和${},接下来讲解的是两者的使用。

1.当参数为Integer类型时

分别选用不同的参数传递方式得到的日志内容:

 

  • #{}形式:

 可以看到传递的参数并没有在后面拼接,id的值使用 ?进行占位,这种SQL称之为 “预编译SQL”

  •  ${}形式:

这种方式的参数是直接拼接在SQL语句中了,这种称之为“即时SQL”。

2.当参数为String类型时

#{}打印的日志正常,而${}这次的参数依然是拼接在SQL语句中,字符串参数作为参数时,需要添加引号,使用${}不会自动的加上引号 ' ',所以导致了程序报错。

所以给参数添加引号:

@Select("select username, `password`, age, gender, phone from user_info where 
username= '${name}' ")
UserInfo queryByName(String name);

再次运行,结果正常了:

 从上面两个例子可以看出:

  • #{ }使用的是预编译SQL,通过 ?  占位的方式,提前对SQL进行编译,然后把参数填充到SQL语句中 ;而#{ } 会根据参数类型,自动拼接引导 ' '
  • ${ }会直接进行字符串替换,一起对SQL进行编译,如果参数为字符串,需要加上引号 ' '。

${ }中,参数为数字类型时,也可以加上查询结果不变,但是会导致索引失败,性能下降


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

#{}和${}的区别就是预编译SQL和即时SQL。

  • 即时SQL流程

当用户发来一条SQL语句给服务器时:

  1. 解析语法和语义,校验SQL语句是否正确
  2. 优化SQL语句,制定执行计划
  3. 执行并返回结果

  • 而相比于即时SQL,预编译SQL往往有下面的优势。

1.#{}性能更高

若一条SQL语句被反复的调用执行时,或者参数值不同时,那么 每次都需要经过上面的语法解析流程,SQL优化,SQL编译等,这样效率就明显不行了。

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

2.#{}更安全(防止SQL注入)

SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行 攻击的方法。

由于没有对用户进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。

  • 使用${}下的sql注入代码:' or 1= ' 1  或者 admin ' --
@Select("select username, `password`, age, gender, phone from user_info where 
username= '${name}' ")
List<UserInfo> queryByName(String name);

若用户输入name 为 ' or 1= ' 1 时,sql拼接后为:

Select("select username, `password`, age, gender, phone from user_info where 
username= ' ' or 1= ' 1' ;

 这时候where中的条件中 1 =' 1', 字符1强制转换为 整型,or的条件为真,那么where的结果就为true,这时候,查询到的结果就为整张表的信息。

  • 使用${}下的sql注入代码: admin ' --
@Select("select username, `password`, age, gender, phone from user_info where 
username= '${name}' and '${password}' ")
List<UserInfo> queryByName(String name,String password);

若用户输入 admin ' -- , 密码随便输入时:

Select("select username, `password`, age, gender, phone from user_info where 
username=  ' admin '-- ' and password ='123';

这时, -- 后面的内容就变为注释,这样一来就可以访问到用户的所有信息了。

  • 但是如果用#{}的情况下,并不会出现这种情况:
@Select("select username, `password`, age, gender, phone from user_info where 
username= #{name}")
List<UserInfo> queryByName(String name);
Select("select username, `password`, age, gender, phone from user_info where 
username=  ' admin '-- ' and password ='123';

传入 admin ' -- 参数之后,会将输入参数视为纯数据而不是sql执行语句。

 可以看到 输入的参数 admin ' -- 参数查询结果为空 没有找到admin ' -- 这样的记录。

所以${}是sql拼接而成的,不能防止sql注入;而#{ }匹配的是一个占位符,相当于JDBC的 ?, 会对一些敏感的字符进行过滤,让参数被视为纯数据而不是sql执语句,其编译后会对传递的值加上双引号,可以防止sql注入的问题。#{}底层采用的是PreparedStatement,会预编译,因此不会产生SQL注入问题)。

3.${}和#{}使用场景

从上面的例子中可以得到:${}会有注入sql的风险,所以尽量使用 #{}完成查询。

排序功能

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

使用${sort}可以实现排序查询,而使用#{sort}就不能实现排序查询了。

注意:此处sort参数为String类型,在sql语句中是不用加'  '号的,但是 #{ }会自动加上,而${ }不需要加上引号。

  • 若把${ }改成#{ }
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +
 "from user_info order by id #{sort} ")
List<UserInfo> queryAllUserBySort(String sort);

运行结果:

可以看到,当使用#{sort}查询时,asc前后自动给加了引号,导致sql错误。

like查询

like查询需要加上引号包裹

  • like使用#{ }报错
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +
 "from user_info where username like '%#{key}%' ")
List<UserInfo> queryAllUserByLike(String key);

 此时,?不再是一个有效的参数占位符,而是一个普通的字符 ' ? ',Mybatis无法绑定参数。

若#{}改成${}可以正确查出来,但是where的条件语句而且${}存在 SQL注入的问题,所以不能直接使用 ${}。

  • 解决办法:使用mysql的内置函数concat()来处理,实现代码如下:
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +
 "from user_info where username like concat('%',#{key},'%')")
List<UserInfo> queryAllUserByLike(String key);

4.总结

  1. #{}:预编译处理,${}:字符直接替换。
  2. #{}可以防止SQL注入,${ }存在sql注入风险,查询语句中,可以使用#{}。
  3. 但是 一些场景,#{}不能完成,比如排序功能,表名、字段名作为参数时,这些情况需要使用#{}。
  4. 模糊查询虽然${}可以完成,但因为存在SQL注入的问题,所以通常使用mysql内置函数concat来 完成。

三、数据库连接池

在上面Mybatis的讲解中,使用了数据库连接池技术,避免频繁地创建连接销毁连接。

1.介绍

数据库连接池负责分配、管理和释放数据连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

  • 没有使用数据库连接池的情况:每次执行SQL语句,要先创建一个连接对象,然后执行SQL语句,SQL语句执行完再关闭连接对象释放资源。这种重复的创建连接和销毁连接比较消耗资源。
  • 使用数据库连接池的情况:程序启动时,会在数据库连接池中创建一定数量的Connection对象,当客户请求数据库连接池时,会从数据库连接池中获取Connection对象,然后执行SQL语句,SQL语句执行完,再把Connection归还给连接池。

优点:

  1. 减少了网络开销
  2. 资源重用
  3. 提升了系统的性能

2.常见的数据库连接池

  • C3P0
  • DBCP
  • Druid
  • Hikari

目前比较流行的是Hikari,Druid

1.Hikari:SpringBoot默认使用的连接数据池:

 2.Druid

如果想把默认的数据库连接池切换为Druid数据库连接池,只需要 引入相关依赖即可。

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-3-starter</artifactId>
  <version>1.2.21</version>
</dependency>

如果SpringBoot版本为2.x,使用druid-spring-boot-start 依赖:

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.1.17</version>
</dependency>

运行结果:


http://www.kler.cn/a/598603.html

相关文章:

  • 计算机网络高频(二)TCP/IP基础
  • 深度学习框架PyTorch——从入门到精通(6.2)自动微分机制
  • git 基础操作
  • 机器学习结合盘古模型与RAMS实现多尺度气象分析与降尺度的程序结构与流程
  • 母婴电商企业案例:日事清驱动项目管理执行与OKR目标管理的流程自动化实践
  • 串口自动化断电测试
  • 神聖的綫性代數速成例題19. 最小二乘法在線性代數中的應用、線性空間的直和分解及相關性質、矩陣的特徵值分解的拓展應用
  • nginx配置https域名后,代理后端服务器流式接口变慢
  • Sqoop 常用命令
  • LeetCode(27):移除元素
  • mybatis操作数据库报错Cause: Cannot find class: ${com.mysql.cj.jdbc.Driver}
  • sgpt 终端使用指南
  • python每日十题(6)
  • 贪心算法(9)(java)最优除法
  • 深入解析 Redis 实现分布式锁的最佳实践
  • 基于Spring Boot的二手物品交易管理系统的设计与实现(LW+源码+讲解)
  • 无人机动平衡-如何在螺旋桨上添加或移除材料
  • 2025年优化算法:龙卷风优化算法(Tornado optimizer with Coriolis force,TOC)
  • 常见中间件漏洞攻略-Jboss篇
  • 社群输出价值重构:AI技术驱动的持续内容生产体系研究