MyBatis基本操作及SpringBoot单元测试

news2024/12/25 23:45:46

目录

一、什么是单元测试?

1.1 单元测试的好处

1.2 单元测试的实现步骤

1.2.1 生成单元测试类:

1.2.2 @SpringBootTest注解

1.2.3 检验方法结果:

二、利用MyBatis实现查询操作

2.1单表查询

2.2 参数占位符 #{} 和 ${}

2.2.1 ${} 字符直接替换

2.2.2 #{} 预编译处理

2.2.3  #{} 和 ${} 两者的区别

2.2.4 SQL注入问题

2.3 like查询 

2.3.1 引入concat解决#{}的问题 

2.3.2 当出现实体类类名与数据库字段名不相同的时候该怎么处理? 

2.4 多表查询

三、利用MyBatis实现修改操作

四、利用MyBatis实现删除操作

五、利用MyBatis实现添加操作


前言:本篇出自博主的上一篇博客快速入门MyBatis,以下操作的数据库皆为上章所提及,这里就不再演示。 

在介绍单元测试之前,先来看一组操作:

以下是根据所给的 ID 来查询用户名:

如果没有使用单元测试的情况下需要验证该功能的正常性,就只能通过Service调用Mapper层,再根据Controller层调用Service层:

 代码实现如下:

一、什么是单元测试?

单元测试(unit testing),是指对软件中的最⼩可测试单元进⾏检查和验证的过程就叫单元测试。

Spring Boot 项⽬创建时会默认单元测试框架 spring-boot-test,⽽这个单元测试框架主要是依靠另⼀个著名的测试框架 JUnit 实现的: 

打开 pom.xml 就可以看到,以下信息是 Spring Boot 项⽬创建是⾃动添加的:

1.1 单元测试的好处

  1. 可以⾮常简单、直观、快速的测试某⼀个功能是否正确。
  2. 使⽤单元测试可以帮我们在打包的时候,发现⼀些问题,因为在打包之前,所以的单元测试必须通过,否则不能打包成功。
  3. 使⽤单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进⾏任何改变的情况下,测试功能。

 针对第二点:

1.2 单元测试的实现步骤

1.2.1 生成单元测试类:

 按照上述步骤之后,就会生成如下代码:

 1.2.2 @SpringBootTest注解

记得加上@SpringBootTest注解,随后完善测试方法的具体实现:

package com.example.demo.mapper;

import com.example.demo.entity.UserEntity;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;
    @Test
    void getUserById() {
        UserEntity user = userMapper.getUserById(1);
        System.out.println(user);
    }
}

1.2.3 检验方法结果:

如果当@Param里面的参数改为uid,其他地方不再进行修改,那么程序会报错嘛?

:会的,以下是运行结果

分析:

具体来说,@Param注解可以应用于方法的参数上,用于指定参数的名称。这个名称会与 SQL 查询语句中的${}或#{}配合使用,以匹配对应的参数。

二、利用MyBatis实现查询操作

2.1单表查询

UserMapper.xml 代码如下: 

 UserMapper.java 如下:

2.2 参数占位符 #{} 和 ${}

  • #{} : 预编译处理。
  • ${}: 字符直接替换。 

预编译处理 :MyBatis 在处理#{} 时,会将SQL中的 #{} 替换为 ? 号,使用PreparedStatement的set方法来赋值。

直接替换:是将MyBatis在处理${} 时,就是把${} 替换成变量的值。

为了更好的观察两者的区别,我们需要打印MyBatis执行的SQL语句,在此之前需要在application.properties中完成以下配置:

#配置MyBatis的xml保存路径
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置打印打印的日志级别(默认的日志级别是info,需要设置为debug才能显示出来)
logging.level.com.example.demo=debug

2.2.1 ${} 字符直接替换

运行以下测试方法进行检查功能是否正确,运行代码如下所示:

 2.2.2 #{} 预编译处理

运行以下测试方法进行检查功能是否正确,运行代码如下所示:

2.2.3  #{} 和 ${} 两者的区别

可能观察了上面这些运行结果,并没有发现这两者之间有什么区别,这是因为之前都是使用int类型进行的传参,当使用String类型进行传参的时候,就会发送改变。

以下为 #{} 占位符模式,可以观察到程序运行正常。

接着我们使用${} 直接替换,观察运行结果,发现程序报错:

细心观察,我们发现:这是因为采用了直接替换的格式,为加任何修饰,于是程序出现了错误,就好比以下SQL代码:

 这是因为采用了直接替换,没有使用引号,于是程序出现了问题,正常的sql语句应该如下:

 或者可以采取这样的方式来避免刚刚的错误:

而${}所采用的直接替换的方式,容易引入SQL注入问题。 【下文会提到】

除此之外,两者还有性能的区别。

使用 #{} 时,参数值会以预编译参数的形式传递给数据库,数据库会对参数进行安全处理。这样可以提高数据库的执行效率,尤其是对于频繁执行的SQL语句,数据库可以重复使用编译好的执行计划,从而提高查询性能。

而使用 ${} 时,参数值会直接替换到SQL语句中,相当于字符串拼接。这样可能导致SQL语句的执行计划在每次执行时都需要重新编译,降低了数据库的执行效率。

因此,从性能角度考虑,推荐使用#{}来处理参数,尽量避免使用${}。特别是当参数值来自用户输入时,使用${}可能存在安全风险,同时也可能降低数据库的执行效率。

可能有的人会问:既然#{}性能比${}高,而且#{}还能防止SQL注入的问题,那么#{}存在的意义是什么?

${}存在的原因是为了一些特殊场景的需求,它提供了更大的灵活性,例如当某些场景需要根据某个数值进行排序的时候:

当使用${}的时候,程序就会正常运行无误:

 小结:

总结起来,#{}是安全的参数占位符,能够防止SQL注入攻击,而${}是简单的字符串替换,需要谨慎使用以避免安全风险。在编写SQL语句时,建议使用#{}来处理参数,尽量避免使用${},特别是当参数值来自用户输入时。

ps:在MyBatis中,无论是#{}还是${},底层的实现都是PreparedStatement,而不是Statement(Statemt执行的是不带参数的SQL语句)。

2.2.4 SQL注入问题

SQL注入常见于登录的时候,以下带来示例:

观察数据库我们可以明白,用户的密码是 admin: 

如果在使用${}的情况下,不输入正确密码,而利用SQL注入,便可绕过验证,获取用户信息: 

 如果是使用#{}则不会出现这样的问题:

这是为什么呢,下面来一组图来解释一下: 

 

 需要注意的是,1是等于符号1的,这里可以利用MySQL语句进行进一步验证:

2.3 like查询 

UserMapper.xml如下所示: 

 UserMapper.java代码如下所示:

测试代码如下:

运行结果:(程序出现错误) 

2.3.1 引入concat解决#{}的问题 

可能有人觉得很奇怪,明明操作没有毛病啊,下面我们来解释一下这个原因,当使用#{}进行处理的时候,实际在Mysql中是转换成这样:

concat的介绍 

  • 在 MySQL 中, concat函数用于将多个字符串连接成一个字符串。它接受两个或多个参数,将它们按顺序连接在一起,并返回连接后的结果。 

为了避免出现多余的单引号,我们可以使用concat对其进行拼接,以下是示例: 

可以观察到,使用concat可以对这些参数进行拼接,这些参数可以是字符串常量、列名或表达式。

以下是使用concat对原先like查询修改后的结果:

观察结果,程序运行正常,且查询到的结果与数据库吻合:

2.3.2 当出现实体类类名与数据库字段名不相同的时候该怎么处理? 

在实际开发的过程中,经常会出现数据库设置的字段名与我们实体类中的名字不相同的情况(需要注意:如果大小写不一致是不影响的):

由于MyBatis是一个ORM框架,如果两者不一致的话(不区分大小写的,例如updateTime就不受影响),是无法进行映射操作的。我们运行测试方法,发现实体类中的pwd属性的结果为空,进一步验证以上观点: 

 此时有两种解决方案,一种是使用Mysql提供的as关键字,另一种是使用resultMap。

①使用as关键字解决上述问题,如下所示:

②使用resultMap:

 这里利用resultMap中的id 和result标签完成映射,拿到了pwd的值:

 分析:

1.id 标签:id 标签用于指定主键列的映射关系。它定义了将查询结果中的某一列映射到目标对象的主键属性上。主键属性通常是唯一标识一个对象的属性。在 id 标签中,需要指定两个属性:

  • property:指定目标对象的属性名,即主键属性名。
  • column:指定查询结果中的列名,即主键列名。

2.result 标签:result 标签用于指定普通属性的映射关系。它定义了将查询结果中的某一列映射到目标对象的普通属性上。普通属性是对象中除主键属性外的其他属性。在 result 标签中,需要指定两个属性:

  • property:指定目标对象的属性名,即普通属性名。
  • column:指定查询结果中的列名,即列名。

2.4 多表查询

输入作者id得到文章详情信息,以下是ArticleMapper.xml实现代码:

 ArticleMapper.java实现代码:

以下是ArticleInfo和ArticleInfoVo的实现:

 运行测试代码,效果如下所示:

分析发现:只有ArticleInfoVo的属性,并没有打印出父类的属性。

初步判断是使用Lombok插件所导致的,让articleinfovo在使用toString方法的时候只打印自己的属性而不打印父类的属性:

 接下来我们查看target目标文件夹下的字节码文件来验证:

通过观察,我们确信是因为我们使用Lombok插件所导致的这一现象。

解决方案:在ArticleInfoVo中重写toString方法(在使用Lombok插件时,如果自己重写的方法与插件所提供的方法相冲突,以自己书写的方法为主):

 Ps:一定要选择重写类型,否则默认情况下重写的toString方法没有继承父类属性。

再次运行测试方法,即可打印父类属性:

三、利用MyBatis实现修改操作

UserMapper.java的代码:

UserMapper.xml的代码:

:在MyBatis的<update>标签中,不需要设置resultType参数的原因是因为<update>标签通常用于执行更新操作(如插入、更新、删除),这些操作的返回结果通常是受影响的行数,而不是具体的结果对象。 

进行测试,效果如下所示:

由于前面都是使用查询操作,并未涉及修改数据库信息等操作,所以在单元测试中未提及@Transactional注解,这里简单介绍一下:

@Transactional注解是用于声明方法或类需要进行事务管理的注解。它可以应用在方法级别或类级别上。

当@Transactional注解被应用在方法上时,它表示该方法需要在事务控制下执行。事务是一种用于保证一组操作要么全部成功执行,要么全部回滚的机制。在方法执行期间,如果发生异常,事务将回滚,否则,事务将提交。

单元测试的方法在加了该注释后,会自动的进行回滚操作,这样保证测试的方法不会污染数据库的数据: 

可以打开Mysql进行进一步验证,上述代码是将用户1的密码123456修改为了1234567:

可以观察到,数据库的数据是未发生变化的。 

四、利用MyBatis实现删除操作

UserMapper.java如下所示: 

 UserMapper.xml如下所示:

进行测试,效果如下所示:

同样的<delete>标签用于执行删除操作,通常用于删除数据库的记录,由于其不返回任何结果,因此在MyBatis中使用delete标签时,不需要设置’resultType‘参数。

Ps:resultType参数用于指定 SQL 语句执行后返回的结果类型,它通常用于查询操作,用于映射查询结果到 Java 对象或其他类型。对于删除操作来说,我们只关心删除的行数,而不需要获取具体的结果对象,因此不需要设置 resultType参数。

五、利用MyBatis实现添加操作

UserMapper.java如下所示:

UserMapper.xml如下所示:

 进行单元测试:

 查询Mysql进行验证,发现确实新增了一条数据: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/579589.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全网最细,Selenium自动化测试项目实战技巧,从0到1精通自动化测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Selenium4自动化测…

重学Ajax

概述 Ajax&#xff08;Asynchronous JavaScript And XML&#xff09;即异步 JavaScript 和 XML&#xff0c;是一组用于在网页上进行异步数据交换的Web开发技术&#xff0c;可以在不刷新整个页面的情况下向服务器发起请求并获取数据&#xff0c;然后将数据插入到网页中的某个位置…

【Docker】Swarm学习

文章目录 什么是Docker Swarm定义与Docker Compose对比 基本结构图关键概念工作模式NodeService任务与调度服务副本与全局服务 Swarm的调度策略Swarm的特性批量创建服务强大的集群的容错性服务节点的可扩展性调度机制 集群部署基础架构准备工作创建Swarm并添加节点在Swarm中部署…

测试进阶篇

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 按照对象划分界面测试可靠性测试容错性测试文档测试兼容性测试易用性测试安装卸载测试安全测试性能测试内存泄漏测试 按是否查…

老板让你Excel统计数据无从下手?没事,ChatGPT来帮你!

系列文章目录 老板让你写个PPT没有头绪&#xff1f;没事&#xff0c;ChatGPT来帮你&#xff01;传送门 文章目录 系列文章目录前言一、不会公式&#xff1f;帮你生成二、不会处理数据&#xff1f;帮你处理写在最后 前言 自从人工智能横空而出&#xff0c;它在人们的生活中产生…

如何在华为OD机试中获得满分?Java实现【 第一个错误的版本】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

【P36】JMeter 交替控制器(Interleave Controller)

文章目录 一、交替控制器&#xff08;Interleave Controller&#xff09;参数说明二、测试计划设计 一、交替控制器&#xff08;Interleave Controller&#xff09;参数说明 可以将内部的组件在线程迭代时交替执行&#xff1b;交替控制器内部一般会有多个取样器 选择线程组右…

6.6.4 PCS创建Oracle 资源及资源组

在RHCS体系中&#xff0c;Oracle的启动是按以下顺序进行的&#xff1a; VIP。监听器。逻辑卷&#xff08;ISCSI共享出来的&#xff09;。文件系统&#xff08;在逻辑卷上创建&#xff09;。数据库实例。 上边这些资源&#xff0c;在PCS里创建好以后&#xff0c;将其组合成一个…

接口自动化一键集成,Jenkins持续集成Allure报告!

目录 前言&#xff1a; 一、接口测试框架选型 二、接口自动化框架封装的设计 2.1 创建测试用例 2.2 执行测试用例 2.3 生成测试报告 三、 实现Jenkins持续集成 3.1 安装Jenkins 3.2 配置Jenkins 3.3 创建Jenkins任务 四、总结 前言&#xff1a; 接口测试作为软件测试中的…

chatgpt赋能python:Python程序中断

Python 程序中断 Python 是一种高级编程语言&#xff0c;被广泛应用于数据科学和机器学习等领域。但是&#xff0c;有时候我们需要中断 Python 程序的执行&#xff0c;以便处理意外事件或者出现错误时进行调试。在这篇文章中&#xff0c;我们将探讨 Python 程序中断的各种方法…

Rocky9-Linux上安装KVM虚拟机

一、案例环境 使用一台物理机器,安装Rocky9-Linux的64位系统,test01是在宿主机kvm中安装的虚拟机 主机 操作系统 IP地址 主要软件 kvm Centos 7 192.168.100.46 KVM test01 Centos 7 192.168.100.32 虚拟机

梅须逊雪三分白,雪却输梅一段香——CSS动画与JavaScript动画

CSS动画并不是绝对比JavaScript动画性能更优越&#xff0c;开源动画库Velocity.js等就展现了强劲的性能。 一、两者的主要区别 先开门见山的说说两者之间的区别。 1&#xff09;CSS动画&#xff1a; 基于CSS的动画一般由浏览器“主线程”之外的独立线程处理&#xff0c;在其…

SpringBoot2-核心技术(一)

SpringBoot2-核心技术&#xff08;一&#xff09; 了解SpringBoot配置文件的使用 文章目录 SpringBoot2-核心技术&#xff08;一&#xff09;了解SpringBoot配置文件的使用一、文件类型1. properties2. yaml 二、yaml的基本使用1. 基本语法2. 数据类型2.1 字面量 2.2 对象2.3 …

Ubuntu系统磁盘分区与挂载

0x00、一些查看信息的命令 sudo fdisk -l 该命令可以列出当前设备上的所有分区表信息。 df -hl 该命令可以查看文件系统磁盘空间的使用情况。 0x01、对磁盘分区 使用命令 sudo fdisk 设备文件名&#xff08;/dev/sda&#xff09; 此处设备路径可以通过上面的fdisk -l命令查看…

测试老鸟总结,性能测试三大核心指标详解,并发/TPS/响应时间...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 抛出问题&#xf…

恒容容器放气的瞬时流量的计算

有时候&#xff0c;你会遇到一个问题&#xff0c;该问题的描述如下&#xff1a; 你有一个已知体积的容器&#xff0c;设容器体积为V&#xff0c;里面装有一定压力(初始压力)的气体&#xff0c;如空气或氢气等&#xff0c;设初始压力为1MPa&#xff0c;容器出口连接着一个阀门开…

服务(第三十一篇)mysql-mmm高可用

MMM是一套支持双主故障切换和双主日常管理的脚本程序。 用来监控和管理 MySQL Master-Master &#xff08;双主&#xff09;复制&#xff0c;虽然叫做双主复制&#xff0c;但是业务上同一时刻只允许对一个主进行写入&#xff0c;另一台备选主上提供部分读服务&#xff0c;以加…

Makefile基础教程(自动生成依赖关系)

文章目录 前言一、makefile不包含.h依赖的后果二、gcc -M 和 gcc -MM命令三、sed命令四、makefile中命令的执行机制四、生成依赖文件并单独放入文件夹中总结前言 在前面的文章中我们都只使用到了.c文件作为依赖但是在实际的工程中肯定是不可能只有.c文件的还存在.h文件,那么在…

CSAPP Lab5- MallocLab

实验目标 本实验需要用c语言实现一个动态的存储分配器&#xff0c;也就是你自己版本的malloc&#xff0c;free&#xff0c;realloc函数。 实验步骤 tar xvf malloclab-handout.tar解压文件 我们需要修改的唯一文件是mm.c&#xff0c;包含如下几个需要实现的函数 int mm_ini…

ChatGPT通用人工智能:初心与未来

至少从 20 世纪 50 年代起&#xff0c;人们就开始大肆宣传可能很快就会创造出一种能够与人类智能的全部范围和水平相匹配的机器。现在&#xff0c;我们已经成功地创造出了能够解决特定问题的机器&#xff0c;其准确度达到甚至超过了人类&#xff0c;但我们仍然无法获得通用智能…