走入MyBatis框架

系列文章目录



前言

MyBatis本是apache的一个开源项目iBatis, 它是一款持久层框架,支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集, 并且可以使用简单的 XML 或注解来配置和映射原生信息, 它的主要作用是快速实现对关系型数据库中的数据进行访问
Mybatis可以不依赖于Spring等框架直接使用的,但是,就需要进行大量的配置,前期配置工作量较大,基于Spring框架目前是业内使用的标准之一,所以,通常会整合Spring与Mybatis,以减少配置


提示:以下是本篇文章正文内容,下面案例可供参考

一、运行环境准备

  1. 安装MySQL数据库
  2. 在IDEA中创建Maven项目
  3. 在Maven项目的pom.xml文件中, 导入以下依赖, 并刷新Maven项目
<dependencies>
<!-- mybatis依赖 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
<!-- mybatis整合spring依赖: mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
</dependency>
<!-- spring依赖项: spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.14</version>
</dependency>
<!-- 基于spring测试依赖项: spring-test 版本需要与spring框架相同-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.14</version>
</dependency>
<!-- spring JDBC依赖项: spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.14</version>
</dependency>
<!-- MySQL数据库依赖: mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>
<!-- 数据库连接池依赖: commons-dbcp2 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.8.0</version>
</dependency>
<!-- Junit测试依赖: junit-jupiter-api -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>
</dependencies>
  1. 创建工程配置文件

src/main/resource下, 创建datasource.properties文件, 并写入:

datasource.url=jdbc:mysql://localhost:3306/mall_ams?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
datasource.driver=com.mysql.cj.jdbc.Driver
datasource.username=root
datasource.password=root
#Mybatis框架配置xml文件操作数据库指定的路径
mybatis.mapper-locations=classpath:mapper/*.xml

src/main/resource,新建mapper包, 并创建TestMapper.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">
<!--namespace路径要指定mybatis要创建的代理对象的接口-->
<mapper namespace="cn.qingtian.mybatis.mapper.TestMapper">
</mapper>

cn.qingtian.mybatis.mapper 下创建TestMapper接口类, 并写入:

package cn.qingtian.mybatis.mapper;

import org.springframework.stereotype.Repository;

@Repository
public interface TestMapper {
}

cn.qingtian.mybatis.config 下创建SpringConfig类, 并写入:

package cn.qingtian.mybatis.config;

import org.apache.commons.dbcp2.BasicDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;

import javax.sql.DataSource;

@Configuration//1.用于声明spring配置类
@ComponentScan("cn.qingtian.mybatis")//1.用于声明spring扫描范围
@PropertySource("classpath:datasource.properties")//2.spring用于指定配置文件路径
@MapperScan("cn.qingtian.mybatis")//4.mybatis框架生成代理对象的扫描范围
public class SpringConfig {

    //3.配置Java的JDBC 连接 可以使java连接数据库, 对象 通过@Bean 交给spring管理, 方法参数由spring容器自动配置
    @Bean
    public DataSource dataSource(Environment env) {
        BasicDataSource basic = new BasicDataSource();
        basic.setUrl(env.getProperty("datasource.url"));
        basic.setDriverClassName(env.getProperty("datasource.driver"));
        basic.setUsername(env.getProperty("datasource.username"));
        basic.setPassword(env.getProperty("datasource.password"));
        return basic;
    }

    //4.配置mybatis框架与java连接, 并将sql会话对象 通过@Bean 交给spring管理, 参数由spring容器配置
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource,@Value("${mybatis.mapper-locations}") Resource... mapperLocations){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(mapperLocations);
        return sqlSessionFactoryBean;
    }
}

src/test/java下, 创建cn.qingtian.mybatis.connection包, 并创建MyBatisTest类写入:

package cn.qingtian.mybatis.connection;

import cn.qingtian.mybatis.config.SpringConfig;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import javax.sql.DataSource;

@SpringJUnitConfig(SpringConfig.class)//5.导入spring-test依赖,可以让测试调用spring环境
public class MyBatisTest {

    // 1.测试启动
    @Test
    public void testRun(){
        System.out.println("cn.qingtian.mybatis.connection.MyBatisEnvironmentTest.testRun");
    }

    // 2.测试Environment能否读取到properties文件中数据
    @Test
    public void testEnvironment(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        Environment environment = ac.getBean(Environment.class);
        System.out.println(environment.getProperty("datasource.url"));
        System.out.println(environment.getProperty("datasource.driver"));
        System.out.println(environment.getProperty("datasource.username"));
        System.out.println(environment.getProperty("datasource.password"));
        System.out.println(environment.getProperty("mybatis.mapper-locations"));
    }

    // 3.测试Java的JDBC连接是否正常, 可以获取连接对象即正常
    @Test
    public void testConnection() {
        Assertions.assertDoesNotThrow(()->{
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
            DataSource dataSource = ac.getBean(DataSource.class);
            Assertions.assertNotNull(dataSource);
        });
    }

    // 4.测试mybatis框架配置是否正确
    @Test
    public void testMybatisConnection() {
        Assertions.assertDoesNotThrow(()->{
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
            SqlSessionFactoryBean bean = ac.getBean(SqlSessionFactoryBean.class);
            Assertions.assertNotNull(bean);
        });
    }

    //5.spring自动装配机制
    @Autowired
    SqlSessionFactoryBean sqlBean;

    // 5.前4项没问题, 测试时加入spring环境
    public void testSpringMybatisConnection() {
         Assertions.assertNotNull(sqlBean);
    }
}

最后项目结构如图:

在这里插入图片描述
5. 测试环境加载是否正常

启动在src/test/javacn.qingtian.mybatis.connectionMyBatisTest测试类

在这里插入图片描述

控制台有有绿色对勾即为测试通过, 红色错误表示断言失败, 测试不通过

没有编写实质的代码,以上测试代码很简单,应该能成功通过测试,如果不能通过测试,可能是开发工具、开发环境、依赖项、项目创建步骤等有问题。

二、基本使用


1.编写代码注意点

当使用Mybatis实现数据访问时, 主要编写以下代码:

  1. 编写访问数据的抽象方法
  2. 配置抽象方法对应的SQL语句

关于配置抽象方法:

  1. 必须定义在某个接口中, 这样的解决通常使用Mapper作为名称的后缀, 例如AdminMapper, mybatis框架底层将通过接口代理模式来实现调用
  2. 抽象方法返回值类型选用: 如果执行增删、改、查类型数据操作, 统一使用int作为返回值类型, 此时返回值表示受影响的行数, 不推荐使用void; 要执行查询数据的操作, 返回值类型只需要能够装载所需的数据就行
  3. 抽象方法名称定义:
    插入数据使用insert作为方法名称中的前缀或关键字
    删除数据使用delete作为方法名称中的前缀或关键字
    更新数据使用update作为方法名称中的前缀或关键字
    查询数据时:
    如果是统计, 使用count作为方法名称中的前缀或关键字
    如果是单个数据, 使用getfind作为方法名称中的前缀或关键字
    如果是列表, 使用list作为方法名称中的前缀或关键字
    如果操作数据时, 有条件, 可以在以上前缀或关键字右侧添加By字段名, 例如deleteById
  4. 抽象方法的参数列表: 是由需要执行的SQL语句中的参数决定, 如果有多个参数, 可将这些参数封装到同一类型中,使用封装的类型作为方法的参数类型,; 多个参数,如果不封装需要在每个参数前添加@Param("参数名称")注解, 因为java编译时, 变量名就会被替换成arg0, arg1..., 会导致调用XML中的SQL语句时参数名错误, 当然直接使用arg0...作为参数名称编译就不会替换了

2.框架识别的接口

所有由于Mybatis处理数据的接口, 都必须被Mybatis识别, 有两种注解可供使用:

  • 每个接口上添加@Mapper注解, 在接口方法中使用@Insert("SQL语句") @Delete("SQL语句") @Update("SQL语句") Select("SQL语句")注解标注, 优点是结构简单, 配置容易, 缺点是SQL语句不能复用, 复杂的SQL语句编写费时间
  • 在配置类上添加@MapperScan("接口类根包的绝对路径")指定接口所在的根包, mybatis会自动扫描根包, 并自动生成包中各个接口中的代理对象注意: 不要向其中添加其他接口文件

最后, Mybatis框架通过SqlSessionFactoryBean对象来完成配置, 并将DataSource和XML文件路径配置给SqlSessionFactoryBean来完成配置

配置XML文件路径时需要在datasource.properties文件中补充配置

mybatis.mapper-locations=classpath:mapper/*.xml

3.缓存机制

缓存通常是一个临时存储的数据,在未来的某个时间点可能会被删除, 通常, 存储缓存数据的位置通常是读写效率较高的, 相比其他"非缓存"的数据有更高的处理效率. 由于缓存的数据通常并不是必须的, 则需要额外消耗一定的存储空间, 同时由于从缓存获取数据的效率更高, 所以是一种牺牲空间, 换取时间的做法, 另外, 从数据库读取数据的效率是非常低下的

Mybatis有 2 种缓存机制 :

  1. 一级缓存
    基于SqlSession的缓存, 又称为"会话缓存", 仅当是同一个会话, 同一个Mapper, 同一个抽象方法(同一个SQL语句), 同样的参数值时有效,;
    一级缓存缓存在集成框架的应用中是默认开启的, 且整个过程不由人为控制(如果是自行得到SqlSession后的操作,可自行清理一级缓存)
  2. 二级缓存
    默认全局开启, 它基于namespce, 所以也称为"namespace缓存",只要是当前namespce中查询出来的结果, 都会根据SQL语句及参数进行缓存, 如需配置需要在SQL语句的XML中添加<cache />节点, 表示当前XML中的所有查询都运行开通二级缓存, 在<select>节点上配置useCache="true",对应的<select>节点的查询结果会被二级缓存处理, 并且, 此查询返回的结果必须实现了Serializable接口的,如果使用了<resultMap>配置封装查询解果, 则必须使用<id>节点来封装主键的映射

无论是一级缓存还是二级缓存, 只要数据发生了写操作(增删改), 缓存数据都会被自动清理. 因为Mybatis的缓存机制过于死板, 所以, 一般在开发实践中不怎么使用, 更多的是使用其他的缓存工具并自行制定缓存策略


4.SQL注入与占位符

SQL注入 select * from admin where username=“admin” and password="?";
以上SQL语句, 如果密码处用户输入 : " or “1”== “1 , SQL语句此时是select * from admin where username=“admin” and password=”" or “1”== “1”; 就会跳过密码直接查询到结果, 与原有语义有差别, 此时发生了SQL注入

在XML文件中配置SQL语句时, 参数传入经常使用占位符#{}:

<!-- List<Admin> listPage(@Param("offset") Integer offset, @Param("size") Integer size); -->
<select id="listPage" resultMap="BaseResultMap">
    select 
    	<include refid="BaseQueryFields" />
    from ams_admin 
    order by id 
    limit #{offset}, #{size}
</select>

以上把#{}换成${}, 查询结果也是完全相同的
但是, 如果是要根据用户名查询用户详情时, 将#{}换为${}会出现错误, 原因是:

  • #{}格式的占位符, Mybatis在处理时会使用预编译的做法, 所以在编写SQL语句时, 不必关心数据类型的问题(例如字符串值不需要添加单引号), 也不存在SQL注入的风险(在语义编译时确认类型),占位符#{}特点只能表示某个值, 不能表示SQL语句片段
  • ${}格式占位符, Mybatis在处理时会先将参数值带入到SQL语句中, 然后再执行编译相关过程, 所以需要关心某些值的数据类型, 存在SQL注入风险, 但是它的优点是灵活, 可以把参数写出SQL语句片段带入原SQL中执行, 可以表示任何SQL语句的片段

所以在一般情况下, 尽可能使用#{}格式的占位符, 即使${}能实现"泛用"效果 ; 如果特殊情况下使用了${}应当使用正则表达式或其他做法避免实现SQL注入问题


5.关于RBAC

简单说明
RBAC = Role Based Access Control (基于角色的访问控制)
RBAC是经典的用户权限设计思路, 在这样的设计中, 会存在 3 种类型: 用户 角色 权限, 权限分配到各个角色上, 用户可以关联某种角色, 进而实现用户与权限相关联, 这样会更利于统一管理若干个用户的权限, 如: 可以在中间表中去操作对应关系, 就不用更改其他表数据, 实现快速统一批量的管理
在RBAC的设计思路中, 用户与角色一般是多对多的关系, 角色与权限也是多对多关系, 所以需要2 张中间表和 另外 3张 表 共5张表去描述这种关系

代码如下(示例):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

6.断言

背景
为了表现程序执行顺序或执行结果, 经常使用打印结果作为输出, 如果项目功能多的话, 此时靠从控制台打印的数据人是看不过来的, 而且容易出错, 所以在此基础上出现了测试类, 断言语句, 这样才符合自动化测试的标准(自动化测试中, 可以一键执行项目中的所有测试方法, 并将测试结果汇总到专门的测试报告文件中)

断言常用的方法(通过调用Assertions类中的静态方法可以对结果进行断言)有:

  1. assertEquals(): 断言匹配(相等)
  2. assertNotEquals(): 断言不匹配(不相等)
  3. assertTrue(): 断言为"真"
  4. assertFalse(): 断言为"假"
  5. assertNull(): 断言为null
  6. assertNotNull(): 断言不为null
  7. assertThrows(): 断言将抛出异常(必须把异常类对象传入)
  8. assertDoesNotThrow(): 断言不会抛出异常

7.关于@Sql注解

背景 : 添加@Sql注解前, 必须在pom.xml中添加spring-test依赖, 此注解可以加载.sql的脚本
作用 : 为了保障可以反复测试, 并且达到预期的结果, 如: 执行删除的测试方法时, 假如数据是存在的, 第一次删除可以成功, 但是之后因为数据已经被删除了, 再执行测试就不会成功, 这时就可以编写一些.sql脚本, 通过脚本向数据表中插入数据, 并在每次测试之前执行此脚本, 即可保证每次测试都是成功的
用法 : @Sql注解可以添加在测试类上, 此时对于当前测试类的每个测试方法都是有效的; 也可以添加在测试方法上, 则只对当前测试方法有效; 如果测试类和方法都添加了@Sql注解, 则以测试方法上的注解生效
除了配置需要执行的.Sql脚本之外, 还可以通过executionPhase属性配置其执行的阶段, 如取值为Sql.ExecutionPhase.AFTER_TEST_METHOD时将使得.sql脚本会在测试方法之后被执行

每个测试方法都可以添加多个@Sql注解

例如:

@Test
@Sql(scripts = {"classpath:truncate.sql", "classpath:insert_data.sql"})
@Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testDeleteByIdSuccess() {
   Long id = 1L;
   int rows = adminMapper.deleteById(id);
   Assertions.assertEquals(1, rows);
}

insert_data.sql脚本示例:

insert into ams_admin (username, password) values ('admin001', '123456');
insert into ams_admin (username, password) values ('admin002', '123456');
insert into ams_admin (username, password) values ('admin003', '123456');
insert into ams_admin (username, password) values ('admin004', '123456');
insert into ams_admin (username, password) values ('admin005', '123456');

truncate.sql脚本示例:

truncate ams_admin;

8.动态SQL

Mybatis中的动态SQL表现为: 根据参数不同, 生成不同的SQL语句
例如: 根据若干个id一次性删除若干条管理数据
SLQ语句大致是:

delete from ams_admin where id in (?,?);

以上SQL语句中的id值的数量对开发人员是未知, 可以使用动态SQL实现此功能
AdminMapper接口中添加抽象方法:

//下面三种都行, 推荐第三种使用list集合
int deleteByIds(Long... ids);
int deleteByIds(Long[] ids);
int deleteByIds(List<Long> ids);

然后在AdminMapper.xml中配置以上的抽象方法映射的SQL语句

<!-- int deleteByIds(List<Long> ids); -->
<delete id="deleteByIds">
    delete from ams_admin where id in (
    	<foreach collection="list" item="id" separator=",">
          #{id}
    	</foreach>
    )
</delete>

以上代码中:

  • <foreach>标签: 用于遍历集合或数组类型的参数对象
  • collection属性: 被遍历的参数对象, 当抽象方法的参数只有1且没有添加@Param注解时, 如果如果参数是List类型, 则此属性的值为list, 如果参数是数组类型(包括可变参数)则此属性值为array;当抽象方法的参数有多个或添加了@Param注解时, 此属性为@Param注解中配置的值
  • item属性: 自定义的名称, 表示遍历过程中每个元素的变量名, 可在<foreach>子级使用#{变量名}表示数据
  • separator属性: 分割符号, 会自动添加在遍历到的各元素之间

不写separator属性, 可以写成#{id},然后在</foreach>后加"-1", 此添加的id不存在, 为了是给语句最后的","配对, 这种也不推荐写

9.XML配置

  • 配置SQL语句的XML文件需要顶部特殊的声明,所以通常是从网上下载或通过复制粘贴得到此类文件
  • 根节点必须是<mapper>,且必须配置namespace,取值为对应的Java接口的全限定名
  • 应该根据要执行的SQL语句不同来选择<insert><delete><update><select>节点,这些节点都必须配置id属性,取值为对应的抽象方法的名称
  • 其中的<delete>节点和<update>节点可以随意替换使用,不推荐, 不考虑“获取自动生成的主键值”的情况,<insert><delete><update>也可以混用,不推荐, 要见名知意,
  • 当插入数据时,当需要获取自动生成的主键值时,需要在<insert>节点上配置useGeneratedKeyskeyProperty属性
  • <select>节点上,必须配置resultMapresultType属性中的其中1个
  • 使用<sql>封装SQL语句片段,并使用<include>进行引用,以实现SQL语句的复用
  • 主键列与属性的映射必须使用<id>节点配置
  • 在1对多、多对多的查询中,集合类型的属性的映射必须使用<collection>子节点配置, 否则查询结果出错
  • 其它列与属性的映射使用<result>节点配置
  • 在单表查询中,列与属性名一致时,可以不必显式的配置,但是,在关联查询中,即使列与属性名称一致,也必须显式的配置出来, 如果不显示赋值结果不会赋值
  • 尽可能的全部使用resultMap,如果查询结果是单一某个数据类型(例如基本数据类型或字符串或某个时间等),则使用resultType

三、操作数据


1.处理流程

  1. 编写SQL语句
    先编写好符合处理业务需求的SQL语句
  2. 编写抽象方法
    在mybatis扫描包下, 创建处理业务接口和处理业务的抽象方法
  3. 编写XML文件
    在与相应接口绑定的XML文件中, 处理编写好的SQL语句
  4. 编写测试方法
    在测试类中, 编写测试方法, 保证业务能够测试通过

2.准备数据

创建名为mall_ams的数据库

create database mall_ams;

ams_admin:管理员表

-- 管理员表:创建数据表
drop table if exists ams_admin;
create table ams_admin (
    id bigint unsigned auto_increment,
    username varchar(50) default null unique comment '用户名',
    password char(64) default null comment '密码(密文)',
    nickname varchar(50) default null comment '昵称',
    avatar varchar(255) default null comment '头像URL',
    phone varchar(50) default null unique comment '手机号码',
    email varchar(50) default null unique comment '电子邮箱',
    description varchar(255) default null comment '描述',
    is_enable tinyint unsigned default 0 comment '是否启用,1=启用,0=未启用',
    last_login_ip varchar(50) default null comment '最后登录IP地址(冗余)',
    login_count int unsigned default 0 comment '累计登录次数(冗余)',
    gmt_last_login datetime default null comment '最后登录时间(冗余)',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
) comment '管理员表' charset utf8mb4;

ams_role:角色表

-- 角色表:创建数据表
drop table if exists ams_role;
create table ams_role (
    id bigint unsigned auto_increment,
    name varchar(50) default null comment '名称',
    description varchar(255) default null comment '描述',
    sort tinyint unsigned default 0 comment '自定义排序序号',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
) comment '角色表' charset utf8mb4;

ams_admin_role:管理员与角色的关联表

-- 管理员角色关联表:创建数据表
drop table if exists ams_admin_role;
create table ams_admin_role (
    id bigint unsigned auto_increment,
    admin_id bigint unsigned default null comment '管理员id',
    role_id bigint unsigned default null comment '角色id',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
) comment '管理员角色关联表' charset utf8mb4;

ams_permission:权限表

-- 权限表:创建数据表
drop table if exists ams_permission;
create table ams_permission (
    id bigint unsigned auto_increment,
    name varchar(50) default null comment '名称',
    value varchar(255) default null comment '值',
    description varchar(255) default null comment '描述',
    sort tinyint unsigned default 0 comment '自定义排序序号',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
) comment '权限' charset utf8mb4;

ams_role_permission:角色与权限的关联表

-- 角色权限关联表:创建数据表
drop table if exists ams_role_permission;
create table ams_role_permission (
    id bigint unsigned auto_increment,
    role_id bigint unsigned default null comment '角色id',
    permission_id bigint unsigned default null comment '权限id',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
) comment '角色权限关联表' charset utf8mb4;

truncate.sql脚本文件 在src/test/resources下创建

truncate ams_admin;

insert_data.sql脚本文件 在src/test/resources下创建

truncate ams_admin;
truncate ams_admin_role;
truncate ams_role;
truncate ams_permission;
truncate ams_role_permission;

insert into ams_admin (username, password) values ('admin001', '123456');
insert into ams_admin (username, password) values ('admin002', '123456');
insert into ams_admin (username, password) values ('admin003', '123456');
insert into ams_admin (username, password) values ('admin004', '123456');
insert into ams_admin (username, password) values ('admin005', '123456');
insert into ams_admin (username, password) values ('admin006', '123456');
insert into ams_admin (username, password) values ('admin007', '123456');
insert into ams_admin (username, password) values ('admin008', '123456');
insert into ams_admin (username, password) values ('admin009', '123456');
insert into ams_admin (username, password) values ('admin010', '123456');
insert into ams_admin (username, password) values ('admin011', '123456');
insert into ams_admin (username, password) values ('admin012', '123456');
insert into ams_admin (username, password) values ('admin013', '123456');
insert into ams_admin (username, password) values ('admin014', '123456');
insert into ams_admin (username, password) values ('admin015', '123456');
insert into ams_admin (username, password) values ('admin016', '123456');
insert into ams_admin (username, password) values ('admin017', '123456');
insert into ams_admin (username, password) values ('admin018', '123456');
insert into ams_admin (username, password) values ('admin019', '123456');
insert into ams_admin (username, password) values ('admin020', '123456');

insert into ams_permission (name, value, description) values
('订单-订单管理-读取', '/pms/order/read', '读取订单数据,含列表、详情、查询等'),
('订单-订单管理-编辑', '/pms/order/update', '修改订单数据'),
('订单-订单管理-删除', '/pms/order/delete', '删除订单数据'),
('商品-商品管理-读取', '/pms/product/read', '读取商品数据,含列表、详情、查询等'),
('商品-商品管理-编辑', '/pms/product/update', '修改商品数据'),
('商品-商品管理-删除', '/pms/product/delete', '删除商品数据'),
('后台管理-管理员-读取', '/ams/admin/read', '读取管理人员数据,含列表、详情、查询等'),
('后台管理-管理员-编辑', '/ams/admin/update', '编辑管理人员数据'),
('后台管理-管理员-删除', '/ams/admin/delete', '删除管理人员数据');

insert into ams_role (name) values
('系统管理员'), ('商品管理员'), ('订单管理员');

insert into ams_role_permission (role_id, permission_id) VALUES
(1,7),(1,8),(1,9),
(2,4),(2,5),(2,6),
(3,1),(3,2),(3,3);

insert into ams_admin_role (admin_id, role_id) values
(1, 1), (1, 2), (1, 3),
(2, 2),
(3, 3);

准备类 (cn.qingtian.mybatis.entity.Admin)

package cn.qingtian.mybatis.entity;

import java.time.LocalDateTime;

public class Admin {
    private Long id;
    private String username;
    private String password;
    private String nickname;
    private String avatar;
    private String phone;
    private String email;
    private String description;
    private Integer isEnable;
    private String lastLoginIp;
    private Integer loginCount;
    private LocalDateTime gmtLastLogin;
    private LocalDateTime gmtCreate;
    private LocalDateTime gmtModified;
    // 以下省略 getter setter toString方法
    // 以下省略 getter setter toString方法
}

准备类 (cn.qingtian.mybatis.vo.AdminLoginVO)

package cn.qingtian.mybatis.vo;

import java.util.List;

public class AdminLoginVO {
    private Long id;
    private String username;
    private Integer isEnable;
    private List<PermissionSimpleVO> permissions;
    // 以下省略 getter setter toString方法
    // 以下省略 getter setter toString方法
}

准备类 (cn.qingtian.mybatis.vo.PermissionSimpleVO)

package cn.qingtian.mybatis.vo;

public class PermissionSimpleVO {
    private String name;
    private String value;
    private String description;
    // 以下省略 getter setter toString方法
    // 以下省略 getter setter toString方法
}

项目文件结构图

在这里插入图片描述

3.普通操作


1.增加数据

需求: 实现 插入一条管理员数据, 并通过断言测试成功

编写SQL语句

insert into ams_admin(id, username, password, nickname, avatar, phone, email, description, is_enable, last_login_ip, login_count, gmt_last_login, gmt_create, gmt_modified) values();

编写抽象方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.entity.Admin;
import org.springframework.stereotype.Repository;

@Repository
public interface AdminMapper {

    // 向管理员表中添加用户
    int insert(Admin admin);
}

编写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">
<!--namespace路径要指定mybatis要创建的代理对象的接口-->
<mapper namespace="cn.qingtian.mybatis.mapper.AdminMapper">
    <!-- int insert(Admin admin) -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into ams_admin(
            <include refid="BasicQueryFiled"/>
        ) values (
        #{username}, #{password}, #{nickname}, #{avatar},
        #{phone}, #{email}, #{description}, #{isEnable}, #{lastLoginIp},
        #{loginCount}, #{gmtLastLogin}, #{gmtCreate}, #{gmtModified}
        )
    </insert>

    <sql id="BasicQueryFiled">
        <if test="true">
            username, password, nickname, avatar,
            phone, email, description, is_enable, last_login_ip,
            login_count, gmt_last_login, gmt_create, gmt_modified
        </if>
    </sql>
</mapper>

编写测试方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.config.SpringConfig;
import cn.qingtian.mybatis.entity.Admin;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.time.LocalDateTime;

@SpringJUnitConfig(SpringConfig.class)
public class AdminTest {

    @Autowired
    AdminMapper adminMapper;

    // 向管理员表中添加用户, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testInsertSecurity(){
        Admin admin = new Admin();
        String username = "管理员123";
        String password = "123456";
        LocalDateTime gmtCreate = LocalDateTime.now();
        admin.setUsername(username);
        admin.setPassword(password);
        admin.setGmtCreate(gmtCreate);
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.insert(admin);
            Assertions.assertEquals(1,rows);
        });
    }
}

执行测试, 观察效果

在这里插入图片描述

2.删除数据

需求: 实现 利用动态SQL实现 根据id随机删除多条管理员数据, 并通过断言测试成功

编写SQL语句

delete from ams_admin where id in (?,?);

编写抽象方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.entity.Admin;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface AdminMapper {

    // 向管理员表中添加用户
    int insert(Admin admin);

    // 根据id随机删除多条管理员数据
    int deleteByIds(List<Long> id);
}

编写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">
<!--namespace路径要指定mybatis要创建的代理对象的接口-->
<mapper namespace="cn.qingtian.mybatis.mapper.AdminMapper">
    <!-- int insert(Admin admin) -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into ams_admin(
            <include refid="BasicQueryFiled"/>
        ) values (
        #{username}, #{password}, #{nickname}, #{avatar},
        #{phone}, #{email}, #{description}, #{isEnable}, #{lastLoginIp},
        #{loginCount}, #{gmtLastLogin}, #{gmtCreate}, #{gmtModified}
        )
    </insert>

    <!-- int deleteByIds(List<Long> id) -->
    <delete id="deleteByIds">
        delete from ams_admin where id in (
            <foreach collection="list" item="id" separator=",">
                #{id}
            </foreach>
        )
    </delete>
    
    <sql id="BasicQueryFiled">
        <if test="true">
            username, password, nickname, avatar,
            phone, email, description, is_enable, last_login_ip,
            login_count, gmt_last_login, gmt_create, gmt_modified
        </if>
    </sql>
</mapper>

编写测试方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.config.SpringConfig;
import cn.qingtian.mybatis.entity.Admin;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@SpringJUnitConfig(SpringConfig.class)
public class AdminTest {

    @Autowired
    AdminMapper adminMapper;

    // 向管理员表中添加用户, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testInsertSecurity(){
        Admin admin = new Admin();
        String username = "管理员123";
        String password = "123456";
        LocalDateTime gmtCreate = LocalDateTime.now();
        admin.setUsername(username);
        admin.setPassword(password);
        admin.setGmtCreate(gmtCreate);
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.insert(admin);
            Assertions.assertEquals(1,rows);
        });
    }

    // 根据id随机删除多条管理员数据, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testDeleteByIdsSecurity(){
        List<Long> ids = new ArrayList<>();
        Random random = new Random();
        int number = random.nextInt(19)+1;
        for (int i = 0; i < number; i++) {
            Long id = (long) (i+1);
            ids.add(id);
        }
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.deleteByIds(ids);
            Assertions.assertEquals(number,rows);
        });
    }
}

执行测试, 观察效果

在这里插入图片描述

3.修改数据

需求: 实现 根据用户id修改管理员表密码, 并通过断言测试成功

编写SQL语句

update ams_admin set password=? where id=?;

编写抽象方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.entity.Admin;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface AdminMapper {

    // 向管理员表中添加用户
    int insert(Admin admin);

    // 根据id随机删除多条管理员数据
    int deleteByIds(List<Long> id);

    // 根据用户id修改管理员表密码
    int updatePassById(@Param("id") Long id,@Param("password") String password);
}

编写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">
<!--namespace路径要指定mybatis要创建的代理对象的接口-->
<mapper namespace="cn.qingtian.mybatis.mapper.AdminMapper">
    <!-- int insert(Admin admin) -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into ams_admin(
            <include refid="BasicQueryFiled"/>
        ) values (
        #{username}, #{password}, #{nickname}, #{avatar},
        #{phone}, #{email}, #{description}, #{isEnable}, #{lastLoginIp},
        #{loginCount}, #{gmtLastLogin}, #{gmtCreate}, #{gmtModified}
        )
    </insert>

    <!-- int deleteByIds(List<Long> id) -->
    <delete id="deleteByIds">
        delete from ams_admin where id in (
            <foreach collection="list" item="id" separator=",">
                #{id}
            </foreach>
        )
    </delete>

    <!-- int updatePassById(@Param("id") Long id,@Param("password") String password) -->
    <update id="updatePassById">
        update ams_admin
            set password=#{password}
        where id=#{id}
    </update>

    <sql id="BasicQueryFiled">
        <if test="true">
            username, password, nickname, avatar,
            phone, email, description, is_enable, last_login_ip,
            login_count, gmt_last_login, gmt_create, gmt_modified
        </if>
    </sql>
</mapper>

编写测试方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.config.SpringConfig;
import cn.qingtian.mybatis.entity.Admin;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@SpringJUnitConfig(SpringConfig.class)
public class AdminTest {

    @Autowired
    AdminMapper adminMapper;

    // 向管理员表中添加用户, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testInsertSecurity(){
        Admin admin = new Admin();
        String username = "管理员123";
        String password = "123456";
        LocalDateTime gmtCreate = LocalDateTime.now();
        admin.setUsername(username);
        admin.setPassword(password);
        admin.setGmtCreate(gmtCreate);
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.insert(admin);
            Assertions.assertEquals(1,rows);
        });
    }

    // 根据id随机删除多条管理员数据, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testDeleteByIdsSecurity(){
        List<Long> ids = new ArrayList<>();
        Random random = new Random();
        int number = random.nextInt(19)+1;
        for (int i = 0; i < number; i++) {
            Long id = (long) (i+1);
            ids.add(id);
        }
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.deleteByIds(ids);
            Assertions.assertEquals(number,rows);
        });
    }

    // 根据用户id修改管理员表密码, 并通过断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testUpdatePassByIdSecurity(){
        Long id = 1L;
        String password = "11111";
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.updatePassById(id,password);
            Assertions.assertEquals(1,rows);
        });
    }
}

执行测试, 观察效果

在这里插入图片描述

4.查询数据

需求: 实现 根据传入页数和显示条数, 通过查询返回管理员表相应数据, 并通过断言测试成功

编写SQL语句

select id, username, password, nickname, avatar, phone, email, description, is_enable, last_login_ip, login_count, gmt_last_login, gmt_create, gmt_modified from ams_admin order by id limit ?,?;

编写抽象方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.entity.Admin;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface AdminMapper {

    // 向管理员表中添加用户
    int insert(Admin admin);

    // 根据id随机删除多条管理员数据
    int deleteByIds(List<Long> id);

    // 根据用户id修改管理员表密码
    int updatePassById(@Param("id") Long id,@Param("password") String password);

    // 根据传入页数和显示条数, 通过查询返回管理员表相应数据
    List<Admin> listPage(@Param("offset") Integer offset,@Param("size") Integer size);
}

编写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">
<!--namespace路径要指定mybatis要创建的代理对象的接口-->
<mapper namespace="cn.qingtian.mybatis.mapper.AdminMapper">
    <!-- int insert(Admin admin) -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into ams_admin(
            <include refid="BasicQueryFiled"/>
        ) values (
        #{id}, #{username}, #{password}, #{nickname}, #{avatar},
        #{phone}, #{email}, #{description}, #{isEnable}, #{lastLoginIp},
        #{loginCount}, #{gmtLastLogin}, #{gmtCreate}, #{gmtModified}
        )
    </insert>

    <!-- int deleteByIds(List<Long> id) -->
    <delete id="deleteByIds">
        delete from ams_admin where id in (
            <foreach collection="list" item="id" separator=",">
                #{id}
            </foreach>
        )
    </delete>

    <!-- int updatePassById(@Param("id") Long id,@Param("password") String password) -->
    <update id="updatePassById">
        update ams_admin
            set password=#{password}
        where id=#{id}
    </update>

    <!-- List<Admin> listPage(@Param("offset") Integer offset,@Param("size") Integer size) -->
    <select id="listPage" resultMap="BasicResultMap">
        select
            <include refid="BasicQueryFiled"/>
        from ams_admin order by id
        limit #{offset},#{size}
    </select>

    <!-- 返回结果通过resultMap指定 -->
    <resultMap id="BasicResultMap" type="cn.qingtian.mybatis.entity.Admin">
        <id column="id" property="id"/>
        <result column="is_enable" property="isEnable"/>
        <result column="last_login_ip" property="lastLoginIp"/>
        <result column="login_count" property="loginCount"/>
        <result column="gmt_last_login" property="gmtLastLogin"/>
        <result column="gmt_create" property="gmtCreate"/>
        <result column="gmt_modified" property="gmtModified"/>
    </resultMap>

    <!-- 封装表字段 -->
    <sql id="BasicQueryFiled">
        <if test="true">
            id, username, password, nickname, avatar,
            phone, email, description, is_enable, last_login_ip,
            login_count, gmt_last_login, gmt_create, gmt_modified
        </if>
    </sql>
</mapper>

编写测试方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.config.SpringConfig;
import cn.qingtian.mybatis.entity.Admin;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@SpringJUnitConfig(SpringConfig.class)
public class AdminTest {

    @Autowired
    AdminMapper adminMapper;

    // 向管理员表中添加用户, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testInsertSecurity(){
        Admin admin = new Admin();
        String username = "管理员123";
        String password = "123456";
        LocalDateTime gmtCreate = LocalDateTime.now();
        admin.setUsername(username);
        admin.setPassword(password);
        admin.setGmtCreate(gmtCreate);
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.insert(admin);
            Assertions.assertEquals(1,rows);
        });
    }

    // 根据id随机删除多条管理员数据, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testDeleteByIdsSecurity(){
        List<Long> ids = new ArrayList<>();
        Random random = new Random();
        int number = random.nextInt(19)+1;
        for (int i = 0; i < number; i++) {
            Long id = (long) (i+1);
            ids.add(id);
        }
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.deleteByIds(ids);
            Assertions.assertEquals(number,rows);
        });
    }

    // 根据用户id修改管理员表密码, 并通过断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testUpdatePassByIdSecurity(){
        Long id = 1L;
        String password = "11111";
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.updatePassById(id,password);
            Assertions.assertEquals(1,rows);
        });
    }

    // 根据传入页数和显示条数, 通过查询返回管理员表相应数据, 并通过断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testListPageSecurity(){
        Integer offset = 4;
        Integer size = 3;
        Assertions.assertDoesNotThrow(()->{
            List<Admin> listPage = adminMapper.listPage(offset, size);
            System.out.println("listPage: "+listPage);
        });
    }
}

执行测试, 观察效果

在这里插入图片描述

4.多表联查

需求: 实现 根据登录用户名查询, 所关联的所有权限, 并通过断言测试成功

编写SQL语句

select
    ams_admin.id,ams_admin.username,ams_admin.is_enable,
    ams_permission.name,ams_permission.value,ams_permission.description
from ams_admin
         left join ams_admin_role on ams_admin.id=ams_admin_role.admin_id
         left join ams_role on ams_admin_role.role_id=ams_role.id
         left join ams_role_permission on ams_role.id=ams_role_permission.role_id
         left join ams_permission on ams_role_permission.permission_id=ams_permission.id
where ams_admin.username=?;

编写抽象方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.entity.Admin;
import cn.qingtian.mybatis.vo.AdminLoginVO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface AdminMapper {

    // 向管理员表中添加用户
    int insert(Admin admin);

    // 根据id随机删除多条管理员数据
    int deleteByIds(List<Long> id);

    // 根据用户id修改管理员表密码
    int updatePassById(@Param("id") Long id,@Param("password") String password);

    // 根据传入页数和显示条数, 通过查询返回管理员表相应数据
    List<Admin> listPage(@Param("offset") Integer offset,@Param("size") Integer size);

    // 根据登录用户名查询, 所关联的所有权限
    AdminLoginVO getLoginInfoByUsername(String username);
}

编写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">
<!--namespace路径要指定mybatis要创建的代理对象的接口-->
<mapper namespace="cn.qingtian.mybatis.mapper.AdminMapper">
    <!-- int insert(Admin admin) -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into ams_admin(
            <include refid="BasicQueryFiled"/>
        ) values (
        #{id}, #{username}, #{password}, #{nickname}, #{avatar},
        #{phone}, #{email}, #{description}, #{isEnable}, #{lastLoginIp},
        #{loginCount}, #{gmtLastLogin}, #{gmtCreate}, #{gmtModified}
        )
    </insert>

    <!-- int deleteByIds(List<Long> id) -->
    <delete id="deleteByIds">
        delete from ams_admin where id in (
            <foreach collection="list" item="id" separator=",">
                #{id}
            </foreach>
        )
    </delete>

    <!-- int updatePassById(@Param("id") Long id,@Param("password") String password) -->
    <update id="updatePassById">
        update ams_admin
            set password=#{password}
        where id=#{id}
    </update>

    <!-- List<Admin> listPage(@Param("offset") Integer offset,@Param("size") Integer size) -->
    <select id="listPage" resultMap="BasicResultMap">
        select
            <include refid="BasicQueryFiled"/>
        from ams_admin order by id
        limit #{offset},#{size}
    </select>

    <!-- AdminLoginVO getLoginInfoByUsername(String username) -->
    <select id="getLoginInfoByUsername" resultMap="LoginInfoByUsernameMap">
        select
            ams_admin.id,ams_admin.username,ams_admin.is_enable,
            ams_permission.name,ams_permission.value,ams_permission.description
        from ams_admin
                 left join ams_admin_role on ams_admin.id=ams_admin_role.admin_id
                 left join ams_role on ams_admin_role.role_id=ams_role.id
                 left join ams_role_permission on ams_role.id=ams_role_permission.role_id
                 left join ams_permission on ams_role_permission.permission_id=ams_permission.id
        where ams_admin.username=#{username};
    </select>

    <!-- 多表查询结果指定 -->
    <resultMap id="LoginInfoByUsernameMap" type="cn.qingtian.mybatis.vo.AdminLoginVO">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="is_enable" property="isEnable"/>
        <collection property="permissions" ofType="cn.qingtian.mybatis.vo.PermissionSimpleVO">
            <result column="name" property="name"/>
            <result column="value" property="value"/>
            <result column="description" property="description"/>
        </collection>
    </resultMap>

    <!-- 返回结果通过resultMap指定 -->
    <resultMap id="BasicResultMap" type="cn.qingtian.mybatis.entity.Admin">
        <id column="id" property="id"/>
        <result column="is_enable" property="isEnable"/>
        <result column="last_login_ip" property="lastLoginIp"/>
        <result column="login_count" property="loginCount"/>
        <result column="gmt_last_login" property="gmtLastLogin"/>
        <result column="gmt_create" property="gmtCreate"/>
        <result column="gmt_modified" property="gmtModified"/>
    </resultMap>

    <!-- 封装表字段 -->
    <sql id="BasicQueryFiled">
        <if test="true">
            id, username, password, nickname, avatar,
            phone, email, description, is_enable, last_login_ip,
            login_count, gmt_last_login, gmt_create, gmt_modified
        </if>
    </sql>
</mapper>

编写测试方法

package cn.qingtian.mybatis.mapper;

import cn.qingtian.mybatis.config.SpringConfig;
import cn.qingtian.mybatis.entity.Admin;
import cn.qingtian.mybatis.vo.AdminLoginVO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@SpringJUnitConfig(SpringConfig.class)
public class AdminTest {

    @Autowired
    AdminMapper adminMapper;

    // 向管理员表中添加用户, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testInsertSecurity(){
        Admin admin = new Admin();
        String username = "管理员123";
        String password = "123456";
        LocalDateTime gmtCreate = LocalDateTime.now();
        admin.setUsername(username);
        admin.setPassword(password);
        admin.setGmtCreate(gmtCreate);
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.insert(admin);
            Assertions.assertEquals(1,rows);
        });
    }

    // 根据id随机删除多条管理员数据, 并用断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testDeleteByIdsSecurity(){
        List<Long> ids = new ArrayList<>();
        Random random = new Random();
        int number = random.nextInt(19)+1;
        for (int i = 0; i < number; i++) {
            Long id = (long) (i+1);
            ids.add(id);
        }
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.deleteByIds(ids);
            Assertions.assertEquals(number,rows);
        });
    }

    // 根据用户id修改管理员表密码, 并通过断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testUpdatePassByIdSecurity(){
        Long id = 1L;
        String password = "11111";
        Assertions.assertDoesNotThrow(()->{
            int rows = adminMapper.updatePassById(id,password);
            Assertions.assertEquals(1,rows);
        });
    }

    // 根据传入页数和显示条数, 通过查询返回管理员表相应数据, 并通过断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void testListPageSecurity(){
        Integer offset = 4;
        Integer size = 3;
        Assertions.assertDoesNotThrow(()->{
            List<Admin> listPage = adminMapper.listPage(offset, size);
            System.out.println("listPage: "+listPage);
        });
    }

    // 根据登录用户名查询, 所关联的所有权限, 并通过断言测试成功
    @Test
    @Sql(scripts = {"classpath:truncate.sql","classpath:insert_data.sql"})
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    public void tesLoginInfoByUsernameSecurity(){
        String username = "admin002";
        Assertions.assertDoesNotThrow(()->{
            AdminLoginVO info = adminMapper.getLoginInfoByUsername(username);
            System.out.println("LoginInfoByUsernameResult: "+ info);
        });
    }
}

执行测试, 观察效果

在这里插入图片描述


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值