网站建设公司合同,个人养老保险账户余额查询,硬件开发用什么语言,wordpress导出模板目录
一、什么是单元测试#xff1f;
1.1 单元测试的好处
1.2 单元测试的实现步骤
1.2.1 生成单元测试类#xff1a;
1.2.2 SpringBootTest注解
1.2.3 检验方法结果#xff1a;
二、利用MyBatis实现查询操作
2.1单表查询
2.2 参数占位符 #{} 和 ${}
2.2.1 ${} 字符…
目录
一、什么是单元测试
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 单元测试的实现步骤
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 {Autowiredprivate UserMapper userMapper;Testvoid 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-implorg.apache.ibatis.logging.stdout.StdOutImpl
#配置打印打印的日志级别(默认的日志级别是info需要设置为debug才能显示出来)
logging.level.com.example.demodebug
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‘参数。
PsresultType参数用于指定 SQL 语句执行后返回的结果类型它通常用于查询操作用于映射查询结果到 Java 对象或其他类型。对于删除操作来说我们只关心删除的行数而不需要获取具体的结果对象因此不需要设置 resultType参数。
五、利用MyBatis实现添加操作
UserMapper.java如下所示 UserMapper.xml如下所示 进行单元测试 查询Mysql进行验证,发现确实新增了一条数据