MyBatis框架之配置MyBatis,单元测试以及MyBatis的简单使用

举报
未见花闻 发表于 2022/08/31 22:30:16 2022/08/31
【摘要】 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

⭐️前面的话⭐️

本篇文章介绍什么是MyBatis?MyBatis的优点,如何配置和使用MyBatis,使用MyBatis实现简单的增删查改功能。

📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创!
📆首发时间:🌴2022年8月31日🌴
✉️坚持和努力一定能换来诗与远方!
💭参考资料:http://www.mybatis.cn/archives/1.html
💬参考在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!

1.MyBatis概述

1.1什么是MyBatis

img

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.2创建项目

第一步,创建SpringBoot项目。
0

1

第二步,勾选上MyBatis相关依赖,包括Mybatis Framework和MySQL Driver。

2
第三步,设置项目名称与路径。
3
第四步,不要着急启动项目,我们还需要配置数据库数据源和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的基本使用流程:

  1. 创建一个使用注解@Mapper修饰的接口,该注解来自MyBatis,作用是与我们配置的Mapper.xml结尾的文件做连接。
  2. 在该接口里面定义方法。
  3. 在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层会告诉持久层到数据库查询数据,持久层查询到数据之后,会将数据交给服务层,然后服务层会将数据交给控制层,最终控制层会将数据打包发送给前端。
1

所以我们还需要建立服务层和控制层来构成完整的后端。

对于服务层,它的任务是调用持久层的方法,去查询数据库,并获得查询的结果。

@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产生映射。

2
我们将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进行查询演示,目前数据库的数据如下:
5
我们启动程序,访问127.0.0.1:8080/user/getuserbyid通过查询字符串的形式将id=1传递给后端进行数据库的查询。

我们得到的结果如下:
8
在控制台也有执行的sql和结果。
9

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即可。

单元测试的类创建在如图目录下:
123333

  • 配置单元测试的类,在类上加上@SpringBootTest注解
  • 添加测试方法,并加上@Test注解
  • 通过注入Mapper对象,调用里面操作数据库的方法,进行单元测试。
    在这里插入图片描述
    122

除了直接输出运行的结果,还可以通过断言来判断测试是否通过,常见断言的方法有如下这一些:
在这里插入图片描述
如通过判断返回的对象引用是否不为null来判断:

    @Autowired
    private UserMapper userMapper;
    @Test
    void gerUserById() {
        UserInfo userInfo = userMapper.getUserById(1);
        //System.out.println(userInfo);
        Assertions.assertNotNull(userInfo);
    }

正常通过显示:
2224

不通过显示:
132

4.3修改

现数据库有以下的数据:
2
现在我们演示如何使用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);
    }

单元测试结果:
3

但是数据库并没有发生修改,原因是@Transactional注解进行了事务的回滚,但是自增主键不会进行回滚,比如你进行了id为3用户插入操作,事务回滚了,下一次插入自增主键id的值为4
4
这也是使用单元测试为什么不会污染数据库的原因。

4.4删除

删除其实和查询,修改也是一样的,我们来演示将id2的数据删除。
第一步,声明方法:

    //根据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);
    }

第四步,查询单元测试结果:
6

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);
    }

第四步,验证结果,单元测试结果:
12
数据库查询结果:

1314
其实,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);
    }

单元测试结果:
1314

数据库查询结果:
2526


下期预告:mybatis多表查询

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。