首页 > 数据库 >Mybatis实战:#{} 和 ${}的使用区别和数据库连接池

Mybatis实战:#{} 和 ${}的使用区别和数据库连接池

时间:2024-08-04 21:54:57浏览次数:16  
标签:username 语句 String 数据库 select SQL Mybatis 连接池

一.#{} 和 ${}

#{} 和 ${} 在MyBatis框架中都是用于SQL语句中参数替换的标记,但它们在使用方式和处理参数值上存在一些显著的区别。

#{}的作用

  1. #{} 是MyBatis中用于预编译SQL语句的参数占位符。
  2. 它会将参数值放入一个预编译的PreparedStatement中,确保参数值被正确地转义和引用,从而防止SQL注入攻击。

特点: 

  • 预编译:数据库驱动在发送SQL语句和参数给DBMS之前对SQL语句进行编译,DBMS执行SQL时不需要重新编译,提高了执行效率。
  • 安全性:由于使用了预编译机制,可以有效防止SQL注入攻击。
  • 类型转换:自动进行Java类型和JDBC类型转换。
  • 适用性:适用于大多数情况,特别是当参数值是从用户输入中获得时,因为它提供了更好的安全性和可靠性。

${}的作用

  • ${} 是MyBatis中用于字符串拼接的参数标记。
  • 它会将参数值直接嵌入到SQL语句中,不进行预编译或转义。

特点:

  • 字符串替换:纯粹的字符串替换,不进行预编译或转义,直接替换成变量的值。
  • 安全性风险:由于不进行预编译或转义,如果不正确地处理参数值,可能会导致SQL注入攻击。
  • 适用性:适用于一些特殊情况,如动态表名、列名或函数调用等,但需要谨慎使用,确保参数值的安全性。

 

1.1Interger类型的参数

1.先看Interger类型的参数
@Select("select username, `password`, age, gender, phone from userinfo where 
id= #{id} ")

UserInfo queryById(Integer id);

2.观察日志

 3.查看日志中的输出语句

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

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

4.我们把 #{} 改成 ${} 再观察打印的⽇志:
@Select("select username, `password`, age, gender, phone from userinfo where 
id= ${id} ")

UserInfo queryById(Integer id);

5.再次查看输出日志信息

可以看到, 这次的参数是直接拼接在SQL语句中了。

1.2 String类型的参数

1.传入String类型的参数

@Select("select username, `password`, age, gender, phone from userinfo where 
username= #{name} ")

UserInfo queryByName(String name);

2.观察我们打印的⽇志, 结果正常返回

3.我们把 #{} 改成 ${} 再观察打印

@Select("select username, `password`, age, gender, phone from userinfo where 
username= ${name} ")

UserInfo queryByName(String name);

4.查看日志 

 

可以看到, 这次的参数依然是直接拼接在SQL语句中了, 但是字符串作为参数时, 需要添加引号 '' , 使⽤ ${} 不会拼接引号 '' , 导致程序报错. 5.修改代码(加上' ')
@Select("select username, `password`, age, gender, phone from userinfo where 
username= '${name}' ")
UserInfo queryByName(String name);

6.查看日志


 我们在IDEA进行代码示范

 1.声明方法中区别

@Select("select * from userinfo where username = #{userName}")
    UserInfo getUserByName(String userName);

@Select("select * from userinfo where username = ${userName}")
    UserInfo getUserByName2(String userName);

2.分别进行单元测试

#运行成功

 $运行失败

为什么呢?

因为#{} 使⽤的是预编译SQL, 通过 ? 占位的⽅式, 提前对SQL进⾏编译, 然后把参数填充到SQL语句中. #{} 会根据参数类型, ⾃动拼接引号 '' 。

${} 会直接进⾏字符替换, ⼀起对SQL进⾏编译. 如果参数为字符串, 需要加上引号 '' 。

 

运行成功



1.3#{} 和 ${}区别 

1.3.1.性能不同

#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别.。 当客⼾发送⼀条SQL语句给服务器后, ⼤致流程如下:
  1. 解析语法和语义, 校验SQL语句是否正确
  2. 优化SQL语句, 制定执⾏计划
  3. 执⾏并返回结果
⼀条 SQL如果⾛上述流程处理, 我们称之为 Immediate Statements(即时 SQL)。
绝⼤多数情况下, 某⼀条 SQL 语句可能会被反复调⽤执⾏, 或者每次执⾏的时候只有个别的值不同(⽐如 select 的 where ⼦句值不同, update 的 set ⼦句值不同, insert 的 values 值不同). 如果每次都需要 经过上⾯的语法解析, SQL优化、SQL编译等,则效率就明显不⾏了.
总之:#预编译SQl的性能更高,是提前占座的行为,$即时SQL是直接拼接的行为。  预编译SQL,编译⼀次之后会将编译后的SQL语句缓存起来,后⾯再次执⾏这条语句时,不会再次编译。 (只是输⼊的参数不同), 省去了解析优化等过程, 以此来提⾼效率.

1.3.2SQL注⼊(面试点)

SQL注⼊:是通过操作输⼊的数据来修改事先定义好的SQL语句,以达到执⾏代码对服务器进⾏攻击的⽅法。
由于没有对⽤⼾输⼊进⾏充分检查,⽽SQL⼜是拼接⽽成,在⽤⼾输⼊参数时,在参数中添加⼀些SQL关键字,达到改变SQL运⾏结果的⽬的,也可以完成恶意攻击。

sql 注⼊代码: ' or 1='1 

1.先来看看SQL注⼊的例⼦:
@Select("select username, `password`, age, gender, phone from userinfo where 
username= '${name}' ")
List<UserInfo> queryByName(String name);
测试代码:
@Test
void queryByName() {
 List<UserInfo> userInfos = userInfoMapper.queryByName("admin");
 System.out.println(userInfos);
}

2.正常访问情况

3.SQL注⼊场景:

@Test
void queryByName() {
List<UserInfo> userInfos = userInfoMapper.queryByNam
 System.out.println(userInfos);
}

 结果依然被正确查询出来了, 其中参数 or被当做了SQL语句的⼀部分

4.依然正确运行

控制层: UserController
import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/login")
    public boolean login(String name, String password) {
        UserInfo userInfo = userService.queryUserByPassword(name, password);
        if (userInfo != null) {
            return true;
        }
        return false;
    }
}
业务层: UserService

import com.example.demo.mapper.UserInfoMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    public UserInfo queryUserByPassword(String name, String password) {
        List<UserInfo> userInfos = userInfoMapper.queryUserByPassword(name,
                password);
        if (userInfos != null && userInfos.size() > 0) {
            return userInfos.get(0);
        }
        return null;
    }
}
数据层: UserInfoMapper
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserInfoMapper {
    @Select("select username, `password`, age, gender, phone from userinfo 
            where username= '${name}' and password='${password}' ")
            List<UserInfo> queryUserByPassword(String name, String password);
}
程序正常运⾏

接下来访问SQL注⼊的代码:

password 设置为 ' or 1='1


在IDEA运行示范

1.方法声明

 @Select("select * from userinfo where username = #{userName}")
    UserInfo selectUserByName(String userName);

    @Select("select * from userinfo where username = '${userName}'")
    UserInfo selectUserByName2(String userName);

2.单元测试

 

 @Test
    void selectUserByName() {
        System.out.println(userInfoMapper.selectUserByName("' or 1='1"));
    }

    @Test
    void selectUserByName2() {
        System.out.println(userInfoMapper.selectUserByName2("' or 1='1"));
    }

#运行:

$运行:

1.3.3排序功能

从上⾯的例⼦中, 可以得出结论: ${} 会有SQL注⼊的⻛险, 所以我们尽量使⽤#{}完成查询 既然如此, 是不是 ${} 就没有存在的必要性了呢? 当然不是.

 比如我进行id升序/降序时。

1.方法声明

使用#

@Select("select * from userinfo order by id #{order}")
    List<UserInfo> queryUserListByOrder(String order);

使用$

 

@Select("select * from userinfo order by id ${order}")
    List<UserInfo> queryUserListByOrder(String order);

 2.单元测试

#测试(失败)

可以发现, 当使⽤ #{sort} 查询时, asc 前后⾃动给加了引号, 导致 sql 错误
#{} 会根据参数类型判断是否拼接引号 '' 如果参数类型为String, 就会加上 引号

 $测试(成功)

使用场景:

使⽤ ${sort} 可以实现排序查询, ⽽使⽤ #{sort} 就不能实现排序查询了.
注意: 此处 sort 参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 '' 的, 所以此时的${sort} 也不加引号

 除此之外, 还有表名作为参数时, 也只能使⽤ ${}。

1.3.4like 查询

LIKE进行模糊查询时,$能直接查询.

like 直接使⽤ #{} 报错

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

把 #{} 改成 ${} 可以正确查出来, 但是${}存在SQL注⼊的问题, 所以不能直接使⽤ ${}.  

实现代码如下: 

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

总结 :

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

二.数据库连接池

在上⾯Mybatis的讲解中, 我们使⽤了数据库连接池技术, 避免频繁的创建连接, 销毁连接 下⾯我们来了解下数据库连接池。 数据库连接池负责分配、管理和释放数据库连接,它允许应⽤程序重复使⽤⼀个现有的数据库连接,⽽不是再重新建⽴⼀个

 

  • 没有使⽤数据库连接池的情况: 每次执⾏SQL语句, 要先创建⼀个新的连接对象, 然后执⾏SQL语句, SQL 语句执⾏完, 再关闭连接对象释放资源. 这种重复的创建连接, 销毁连接⽐较消耗资源
  • 使⽤数据库连接池的情况: 程序启动时, 会在数据库连接池中创建⼀定数量的Connection对象, 当客⼾请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执⾏SQL, SQL语句执⾏完, 再把Connection归还给连接池.
优点 : 1. 减少了⽹络开销 2. 资源重⽤ 3. 提升了系统的性能 常⻅的数据库连接池:
  • C3P0
  • DBCP
  • Druid
  • Hikari
⽬前⽐较流⾏的是 Hikari, Druid 1.Hikari : SpringBoot默认使⽤的数据库连接池

 Hikari 是⽇语"光"的意思(ひかり), Hikari也是以追求性能极致为⽬标

2. Druid

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

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

 运行结果:

Druid连接池是阿⾥巴巴开源的数据库连接池项⽬ 功能强⼤,性能优秀,是Java语⾔最好的数据库连接池之⼀。

标签:username,语句,String,数据库,select,SQL,Mybatis,连接池
From: https://blog.csdn.net/LHY537200/article/details/140866478

相关文章

  • 守护数据堡垒:SQL Server数据库自定义备份审计实现指南
    标题:守护数据堡垒:SQLServer数据库自定义备份审计实现指南引言数据库备份是确保数据安全和业务连续性的关键措施。SQLServer提供了多种备份策略,但有时候,为了满足特定的合规性要求或业务需求,我们需要实现更细粒度的自定义数据备份审计。本文将详细介绍如何在SQLServer中......
  • 索引的艺术:SQL Server数据库性能优化的索引设计策略
    索引的艺术:SQLServer数据库性能优化的索引设计策略在SQLServer数据库的浩瀚世界中,索引是提升查询性能的一把利剑。然而,索引的设计和使用并非一蹴而就,而是需要深思熟虑的策略。本文将深入探讨SQLServer中数据库性能优化时需要考虑的索引设计因素,带你领略索引设计的智慧。......
  • 详细教程 MySQL 数据库 下载 安装 连接 环境配置 全面
    数据库就是储存和管理数据的仓库,对数据进行增删改查操作,其本质是一个软件。首先数据有两种,一种是关系型数据库,另一种是非关系型数据库。关系型数据库是以表的形式来存储数据,表和表之间可以有很多复杂的关系,比如:MySQL、Oracle、SQLServer等;非关系型数据库是以数据集的形式存......
  • 在Spring Boot和MyBatis-Plus项目中,常见的错误及其解决方法
    在SpringBoot和MyBatis-Plus项目中,常见的错误及其解决方法包括以下几种:1.java.sql.SQLSyntaxErrorException:Unknowncolumn'column_name'in'fieldlist'现象SQL语法错误,通常是因为查询的列名在数据库中不存在。解决方法确认数据库中的列名与代码中的列名一致。检查......
  • SSM电子政务系统tedg5 本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表系统内容:公民用户,政务人员,会议通知,政务公告,紧急上报,意见收集,投票,人力资源,应聘开题报告内容一、选题背景与意义随着信息技术的飞速发展和互联网的普及......
  • Springboot计算机毕业设计城市地铁线路与站点查询系统+程序+源码+数据库+调试部署+开
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,车次查询,站点信息,附近公交信息开题报告内容一、研究背景与意义随着城市化进程的加快和人口的不断增长,城市交通问题日益突出。城市轨道交通作为一种高效......
  • SSM大学生网上书店959u2 本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表系统内容:用户,学院信息,标签信息,书籍分类,书籍信息开题报告内容一、课题名称SSM大学生网上书店系统设计与实现二、研究背景与意义随着互联网和电子商务的......
  • dedecms错误警告:连接数据库失败,可能数据库密码不对或数据库服务器出错
    描述:“dedecms错误警告:连接数据库失败,可能数据库密码不对或数据库服务器出错”如图:分析:用织梦程序制作的站点做迁移服务器后容易出现这个问题,原因是程序中设置的数据库连接信息有误连接不到数据库,解决方法:将根目录下/data/common.inc.php文件中的数据库连接信息修改正确,如图,请......
  • 推荐3款免费且实用的数据库管理工具
    前言数据库在日常工作中确实扮演着至关重要的角色,无论是数据管理、分析还是应用开发,都离不开它的支持。今天大姚给大家分享3款免费且实用的数据库管理工具,希望可以帮助到有需要的同学。DBeaverDBeaver是一款免费的跨平台数据库工具,适用于开发人员、数据库管理员、分析师和所有......
  • 数据库读写分离和分库分表
    读写分离读写分离主要是为了将对数据库的读写操作分散到不同的数据库节点上。一般情况下,我们都会选择一主多从,也就是一台主数据库负责写,其他的从数据库负责读。主库和从库之间会进行数据同步,以保证从库中数据的准确性。这样的架构实现起来比较简单,并且也符合系统的写少读多......