MyBatis框架之配置MyBatis,单元测试以及MyBatis的简单使用
⭐️前面的话⭐️
本篇文章介绍什么是MyBatis?MyBatis的优点,如何配置和使用MyBatis,使用MyBatis实现简单的增删查改功能。
📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创!
📆首发时间:🌴2022年8月31日🌴
✉️坚持和努力一定能换来诗与远方!
💭参考资料:http://www.mybatis.cn/archives/1.html
💬参考在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
1.MyBatis概述
1.1什么是MyBatis
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
MyBatis工作原理:就是对JDBC进行了封装。
MyBatis主要依赖于下面几个组件:
1、SQLSessionFactoryBuilder(构造器):它会根据配置信息或者代码生成SqlSessionFactory。
2、SqlSessionFactory(工厂接口):依靠工厂生成SqlSession。
3、SqlSession(会话):是一个既可以发送SQL去执行并且返回结果,也可以获取Mapper接口。
4、SQL Mapper:是由一个JAVA接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则。SQL是由Mapper发送出去,并且返回结果。
所以我们使用MyBatis的时候需要创建Mapper层接口以及对应的xml
文件,注解可以代替xml
文件进行写SQL,但是SQL语句复杂的时候使用注解写SQL很奇怪,本文着重介绍使用xml
文件写SQL。
1.2MyBatis的优点
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。MyBatis是对JDBC的封装。相对于JDBC,MyBatis有以下优点:
- 优化获取和释放
- SQL统一管理,对数据库进行存取操作
- 支持生成动态SQL语句,针对非必传参数可以动态调整SQL
- 能够对结果集进行映射
2.MyBatis项目的创建
2.1准备一个数据表
准备工作,创建数据库与数据表。
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使用数据数据
use mycnblog;
-- 创建表[用户表]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default 'default.png',
createtime datetime default now(),
updatetime datetime default now(),
`state` int default 1
) default charset 'utf8mb4';
-- 创建文章表
drop table if exists articleinfo;
create table articleinfo(
id int primary key auto_increment,
title varchar(100) not null,
content text not null,
createtime datetime default now(),
updatetime datetime default now(),
uid int not null,
rcount int not null default 1,
`state` int default 1
)default charset 'utf8mb4';
-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
vid int primary key,
`title` varchar(250),
`url` varchar(1000),
createtime datetime default now(),
updatetime datetime default now(),
uid int
)default charset 'utf8mb4';
-- 添加一个用户信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);
-- 文章添加测试数据
insert into articleinfo(title,content,uid)
values('Java','Java正文',1);
-- 添加视频
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);
下面我们主要是要userinfo表进行操作演示,目前执行上述sql后,表内的数据如下:
2.2创建项目
第一步,创建SpringBoot项目。
第二步,勾选上MyBatis相关依赖,包括Mybatis Framework和MySQL Driver。
第三步,设置项目名称与路径。
第四步,不要着急启动项目,我们还需要配置数据库数据源和MyBatis xml文件保存路径,其中这个xml文件是用来编写sql语句用的。
首先,我们创建配置文件,用来链接数据源。
在企业的项目中最少有3个配置文件,一个主配置文件,另外两个配置文件分别用于开发环境和生产环境,可能还会有测试环境的配置文件,所以我们先创建配置文件,至少得有主配置文件和开发环境的配置文件。
在主配置文件中应用dev
配置文件和设置MyBatis xml文件的路径:
# 当前运行环境的配置文件
spring:
profiles:
active: dev
# 配置 mybatis xml 保存路径
mybatis:
mapper-locations: classpath:mybatis/**Mapper.xml
其中**Mapper.xml
只认Mapper.xml
结尾的文件。
我们在开发环境中,配置数据库的url,账号与密码以及驱动,为什么要在开发环境的配置文件配置而不在主配置文件配置呢?这是因为开发环境链接的是这个数据库,但生产环境,测试环境链接的就不是开发环境中的数据库,因此我们将不同的环境使用不同的配置文件来配置,这样仅需改动主配置文件一行的代码就可以了。
# 配置数据库信息
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8
username: root
password: 123456
# 版本8之前版本的数据库使用 com.mysql.jdbc.Driver
# 版本8以及之后版本的数据库使用 com.mysql.cj.jdbc.Driver
driver-class-name: com.mysql.jdbc.Driver
如果需要更方便观察有关sql的运行情况,还可以配置一些日志文件,来记录相关的日志:
# 开启 MyBatis SQL 打印
logging:
level:
com:
example:
demo: debug
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
这样我们的项目就创建好了,下面来介绍MyBatis的用法。
3.MyBatis的使用
MyBatis的基本使用流程:
- 创建一个使用注解@Mapper修饰的接口,该注解来自MyBatis,作用是与我们配置的
Mapper.xml
结尾的文件做连接。 - 在该接口里面定义方法。
- 在xml文件中按照MyBatis的规矩编写sql,xml文件的作用是生成数据库可执行的sql,并且能将结果映射到程序的对象中。
第一步,我们定义接口,该接口在软件分层中属于Mapper
层,所以我们定义在自建的mapper
包下,为了提高代码的可读性和规范性,接口名称建议Mapper
结尾。
@Mapper
public interface UserMapper {
//方法声明
}
第二步,我们在该接口下声明方法,由于该方法的目的就是为了被xml
文件“实现”,然后去操作数据库的,所以方法名建议与数据库的操作有关,比如我们需要根据id
查询User
对象的结果,我们方法名可以声明为getUserById
。
//根据id查询
public UserInfo getUserById(@Param(value = "id") Integer id);
@Param注解中的value
值表示对变量重命名,它表示该变量在xml文件下的变量名,建议该名字与数据库中的字段相对应并同名。
第三步,在配置文件所规定的目录下创建Mapper.xml
文件,并加上下面的代码,一般一个xml负责一个表的增删查改操作:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
</mapper>
当我们要编写sql时,需要在<mapper>
标签里面写对应的标签来实现对数据库的操作,比如查询使用select
标签,插入使用insert
标签,更新使用pudate
标签,删除使用delete
标签等。
而在mapper
标签中,我们需要设置该mapper
标签随对应的接口在哪里,就是在mapper
标签头中设置namespace
属性,值为加包名的Mapper
接口,如:
<mapper namespace="com.example.demo.mapper.UserMapper">
</mapper>
比如根据id查询用户信息,其中标签头的id
属性与要实现的方法名对应,resultType
表示需要映射的类是哪一个,需要写出完整的包名,这样就会将查询的结果存入到该类的对象中对对象数组中,有一点是需要注意的,那就是使用resultType
映射对象一定得保证数据库中的字段名与对象中的属性名一模一样,否则不能匹配赋值。
<select id="getUserById" resultType="com.example.demo.model.UserInfo">
select * from userinfo where id=${id}
</select>
MyBatis实现的是程序中对象与数据库的映射,是程序与数据库的一座桥梁,MyBatis可以通过程序员写的代码去操作数据库,因此它处在软件分层中的Repository持久层,我们知道当后端Controller层收到前端数据库查询的请求后会去调用Service层,Service层会告诉持久层到数据库查询数据,持久层查询到数据之后,会将数据交给服务层,然后服务层会将数据交给控制层,最终控制层会将数据打包发送给前端。
所以我们还需要建立服务层和控制层来构成完整的后端。
对于服务层,它的任务是调用持久层的方法,去查询数据库,并获得查询的结果。
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public UserInfo getUserById(Integer id) {
return userMapper.getUserById(id);
}
}
对于控制层,它需要拿到服务层的对象,调用服务层的方法得到查询的结果。
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/getuserbyid")
public UserInfo getUserById(Integer id) {
if (id == null) return null;
return userService.getUserById(id);
}
}
当然还少不了数据库表对于的类,如UserInfo表对应一个UserInfo类。
@Data
public class UserInfo {
private Integer id;
private String username;
private String password;
private String photo;
private String createtime;
private String updatetime;
private Integer state;
}
4.使用MyBatis实现单表查询
创建好Mapper接口和对应的xml文件后,我们就可以开始写数据库sql的语句了。
4.1查询
第一步,在Mapper接口中声明一个方法,比如通过用户ID查询用户的全部信息,就可以声明一个方法getUserById
。
public UserInfo getUserById(@Param(value = "id") Integer id);
@Param注解的value
参数就是将传入的变量在xml文件中的名字。
第二步,设置xml文件所映射的Mapper接口,假设我们映射了一个如下图mapper包中的UserMapper
接口,该接口一定要使用@Mapper修饰,否则不是属于MyBatis中的“接口”,不能与xml产生映射。
我们将mapper标签中的namespace
设置为对应接口在java目录下的带包全称。
<mapper namespace="com.example.demo.mapper.UserMapper">
第三步,设置查询标签以及配置与方法的映射,查询操作使用select
标签,查询标签至少设置两个属性,第一个是id
表示与哪一个方法相对应,就是设置成相关联的方法的方法名,比如我们在Mapper接口中的查询方法是getUserById
,则设置id="getUserById"
;
还有一个就是resultType
我们查询需要返回查询结果UserInfo
对象,如果可能有多个结果,对应的方法返回值就写List<UserInfo>
,否则写UserInfo
即可,但在查询标签中resultType
属性只需要设置成带包名的类名即可。
<select id="getUserById" resultType="com.example.demo.model.UserInfo">
第四步,在mapper标签中编写查询sql,查询sql对应的标签就是select
标签,如我们要查询id为1
的用户,那么标签里面就这么写:
<select id="getUserById" resultType="com.example.demo.model.UserInfo">
select * from userinfo where id=1
</select>
但是这样写太死了,我们可以说使用预处理符#{}
或者替换符${}
来将程序传入的参数替换到sql语句中:
<select id="getUserById" resultType="com.example.demo.model.UserInfo">
select * from userinfo where id=${id}
//or select * from userinfo where id=#{id}
</select>
其中id=${id}
相当于id=传入的id变量的值
,比如id=1
,则${id}
会被替换为1
,使用该方法存在SQL注入的问题,并且传入字符串等其他非数值类型可能会出现问题,而id=#{id}
相当于id=?
,相当于替换成一个占位符,然后会将传入的id
通过占位符的形式插入到sql语句中,可以防止SQL注入问题,并且适用于所有类型的变量。
有关${}
与#{}
我会另写一篇文章专门来介绍,它们之间的区别,以及它们各自适合的应用场景。
经过以上步骤我们的sql就写好了,由于我们的id
是唯一的,所以查询到的结果也是唯一的,我们以id=1
进行查询演示,目前数据库的数据如下:
我们启动程序,访问127.0.0.1:8080/user/getuserbyid
通过查询字符串的形式将id=1
传递给后端进行数据库的查询。
我们得到的结果如下:
在控制台也有执行的sql和结果。
4.2单元测试
4.2.1单元测试概述
单元测试优点:
1、可以非常简单、直观、快速的测试某个功能是否正确。
2、使用单元测试可以帮我们在打包的时候,发现一些问题,因为在打包之前,所以的单元测试必须通过,否则不能打包成功。
3、使用单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进行任何改变的情况下,测试功能的正确性。
4.2.2SpringBoot单元测试的使用
第一步,添加框架的支持,创建spring boot项目时,会自动添加。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
第二步,我们在类中代码右键,选择Generate
选项,比如我们在Mapper接口中生成单元测试的类,我们就在右键选择Generate
选项。
然后会跳出一个框,选择Test。
第三步,进行配置Test,和选择需要测试的方法。
第四步,第三步勾选上需要测试的方法后,会在test目录生成一个同级的目录,会自动生成测试类和测试方法,当然我们也能够手动地写,创建的测试类就在原来类名最后加上一个Test
即可。
单元测试的类创建在如图目录下:
- 配置单元测试的类,在类上加上@SpringBootTest注解
- 添加测试方法,并加上@Test注解
- 通过注入Mapper对象,调用里面操作数据库的方法,进行单元测试。
除了直接输出运行的结果,还可以通过断言来判断测试是否通过,常见断言的方法有如下这一些:
如通过判断返回的对象引用是否不为null来判断:
@Autowired
private UserMapper userMapper;
@Test
void gerUserById() {
UserInfo userInfo = userMapper.getUserById(1);
//System.out.println(userInfo);
Assertions.assertNotNull(userInfo);
}
正常通过显示:
不通过显示:
4.3修改
现数据库有以下的数据:
现在我们演示如何使用MyBatis根据id
修改对应用户名username
,其实和查询操作是一样的,甚至还要更简单一点,而对于结果的返回值,我们返回受影响的行数,此时我们是不用设置resultType
的。
第一步,声明方法:
//根据id修改名字
public Integer updateName(@Param(value = "id") Integer id, @Param(value = "username") String username);
第二步,在xml中写sql,修改操作使用update
标签。
<update id="updateName" >
update userinfo set username=#{username} where id=#{id}
</update>
我们将id=2的用户名修改为李四,并且使用@Transactional注解可以防止污染数据库,实现原理就是利用了数据库事务的回滚,代码:
@Test
@Transactional
void updateName() {
int result = userMapper.updateName(2, "李四");
Assertions.assertSame(1,result);
}
单元测试结果:
但是数据库并没有发生修改,原因是@Transactional注解进行了事务的回滚,但是自增主键不会进行回滚,比如你进行了id为3
用户插入操作,事务回滚了,下一次插入自增主键id
的值为4
:
这也是使用单元测试为什么不会污染数据库的原因。
4.4删除
删除其实和查询,修改也是一样的,我们来演示将id
为2
的数据删除。
第一步,声明方法:
//根据id删除用户
public Integer delById(@Param(value = "id") Integer id);
第二步,在xml文件编写sql,删除操作使用delete
标签:
<delete id="delById">
delete from userinfo where id=#{id}
</delete>
第三步,编写单元测试验证:
@Test
@Transactional
void delById() {
int result = userMapper.delById(2);
Assertions.assertSame(1,result);
}
第四步,查询单元测试结果:
4.5插入
该操作相比于之前的查询,修改和删除操作要复杂一点,但其实也差不多,为了提高代码的通用性,我们声明方法中的形参传入一个UserInfo
对象。
首先,我们来演示插入一条数据,返回一个受影响的行数。
第一步,声明方法:
//添加用户,返回影响行数
public Integer add(UserInfo userInfo);
第二步,编写sql语句,插入操作使用insert
标签。
<insert id="add">
insert into userinfo(username, password, photo) values (#{username}, #{password},#{photo});
</insert>
第三步,编写单元测试,这次我们不用@Transactional注解,直接看数据库明显一点。
@Test
void add() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("老六");
userInfo.setPassword("888");
int result = userMapper.add(userInfo);
Assertions.assertSame(1,result);
}
第四步,验证结果,单元测试结果:
数据库查询结果:
其实,Mybatis也可以返回多个参数,比如返回受影响的行数和自增id的值,这个时候我们就需要对xml的insert
标签进行配置。
前面声明方法还是需要的:
public Integer addGetId(UserInfo userInfo);
xml的insert
标签中还需要设置useGeneratedKeys="true"
表示为是否自增主键,keyProperty="id"
表示自增主键是哪一个。
<insert id="addGetId" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into userinfo(username, password, photo) values (#{username}, #{password},#{photo});
</insert>
单元测试方法:
@Test
void addGetId() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("老六");
userInfo.setPassword("888");
userInfo.setPhoto("default.png");
System.out.println("插入之前的id=" + userInfo.getId());
int result = userMapper.addGetId(userInfo);
System.out.println("插入之后的id=" + userInfo.getId());
Assertions.assertSame(1,result);
}
单元测试结果:
数据库查询结果:
下期预告:mybatis多表查询
- 点赞
- 收藏
- 关注作者
评论(0)