消息队列的模拟实现(二)

消息队列代码部分-(1)

框架使用:Spring Boot,Mybatis

软件:IDEA 2022.3.3社区版

使用技术:Java、SQLite

一、构思项目的组成

详情查看链接: 消息队列的模拟实现(一)
首先,实现消息队列这里主要分为三个模块:

  1. 公共模块
  • 主要存储公共类
  1. 服务器模块
  • 主要存储服务器方法,比如读取请求返回响应数据
  1. 客户端模块
  • 主要存储客户端方法,比如请求连接方法和构造请求

根据上面的描述,我们创建一个Spring boot来搭建我们的项目。

创建项目后添加依赖

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.3.1</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>2.3.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
        <!-- 这个依赖是用于与 sqlite 数据库进行交互的 Java 库。它提供了一组用于连接,查询和操作SQLite数据库的API和工具-->
		<dependency>
			<groupId>org.xerial</groupId>
			<artifactId>sqlite-jdbc</artifactId>
			<version>3.41.2.1</version>
		</dependency>
	</dependencies>

添加依赖后创建文件夹目录:

在主目录下创建三个文件夹

  • common :公共类模块
  • mqclient :客户端模块
  • mqserver 服务器模块

!在这里插入图片描述

因为公共模块是创建总和其他模块公共方法,不宜作为第一选项实现模块,所以这里我们选择服务器的实现。

建立交换机表、队列表和绑定表
  1. 设计数据库表

对照核心类设置表,并理解属性的使用位置和属性含义。

Exchange

交换机表需要的属性也是根据我们需要的功能进行展开:

  • exchangeName :用于识别交换机
  • type:上面我们提到了交换机有三种类型、direct、fanout和topic
  • durable:持久化,数据的持久化
  • autodelete : 无人使用是否删除,节约内存空间
  • argument: argument中是创建一些交换机指定的一些额外的参数选项,后续代码中并没有对应的参数,相对于一个扩展参数

mqServer创建一个文件夹用于存储核心类,该核心主要是一些实体类。

分析完交换机的字段,创建一个交换机类:

import lombok.Data;
import java.util.HashMap;
import java.util.Map;

@Data//添加setter和getter 方法
public class Exchange {
    private String name;
    private ExchangeType type = ExchangeType.DIRECT;
    private boolean durable = false;//持久化存储
    private boolean autoDelete = false;//自动删除
    //扩展属性
    private Map<String,Object> arguments = new HashMap<>();
}

在创建交换机时,我们将设定交换机类型,这里建议使用枚举,创建一个交换机类型枚举。取名为ExechangeType同样是放在核心文件夹中。

//这个类用于存储三种类型
public enum ExchangeType {
    DIRECT(0),
    FANOUT(1),
    TOPIC(2);
    private final int type;

    ExchangeType(int type) {
        this.type = type;
    }
}
Queue

接下来分析队列需要的字段名称:

  • queueName:队列名,用于识别队列
  • durable :数据是否需要持久化设置
  • exclusive :独占功能,开启独占只能给一个消费者使用
  • autoDelete:无人使用是否删除,节约内存空间
  • argument:扩展参数,先不实现

同样的在核心文件夹创建一个queue类,这里需要注意这个名字与Java中的集合类名字相同,这里因为是存储消息的队列,所以改名为MSGQueue

import lombok.Data;
import java.util.HashMap;
import java.util.Map;

@Data
public class MSGQueue {
    private String name;//队列名称
    private boolean durable;//队列持久化设置
    private boolean exclusive;//对列只能属于一个消费者
    private boolean autoDelete;//对不使用的队列自动删除
    //扩展功能
    private Map<String,Object> argument = new HashMap<>();
}
Binding

要分析绑定,就应该知道绑定的作用,绑定的作用是用来将交换机和队列进行绑定,更好的传输数据。

  • exchangeName:交换机名称
  • queueName:队列名称
  • bindingKey :当交换机为topic时使用匹配时的机制
import lombok.Data;

@Data
public class Binding {
    private String exchangeName;//绑定的交换机名称
    private String queueName;//绑定的队列名称
    private String bindingKey;//当交换机是topic类型时需要进行匹配时使用的字段
}
Message

Message 中需要实现Serializable接⼝.

这个接口是用来管理序列化和反序列化。对象的序列化是指将对象转换成字节流的过程,可以将序列化的对象存储到硬盘中或通过网络传输。而反序列化是将字节流转化为对象的过程。

后续需要把 Message? 写⼊⽂件以及进⾏⽹络传输

  • BasicProperties :消息属性,需要额外写一个类表示
  • byte[] : 消息体,消息的具体内容
  • offsetBeg :消息体开始的地方在文件中相当于一个下标
  • offsetEnd:消息结束的地方
  • isValid:标记消息的有效性,0x1 是有效 0x0是无效
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import javax.xml.validation.ValidatorHandler;
import java.io.Serializable;
import java.util.UUID;

@Data
public class Message implements Serializable {
    //验证开发者版本号
    private static final long serialVersionUID = 1L;
    //Message中核心部分
    private BasicProperties basicProperties = new BasicProperties();
    private byte[] body;//正文部分
    /*
    使用两个偏移量来表示某个消息存储在文件中的位置
    使用前闭后开模式[offsetBeg,offsetEnd]
     */
    private long offsetBeg = 0;//开始
    private long offsetEnd = 0;//结尾
    //使用一个变量标记文件是否有效
    // 0x1 -> 有效  0x0 -> 无效
    private byte isValid = 0x1;
    //创建一个工厂方法,让工厂方法帮我们封装一个 Message 对象
    //在这个方法中获取一个UUID 以 M- 作为消息前缀
    public static Message createMessage(String routingKey,BasicProperties basicProperties,byte[] body){
        Message message = new Message();
        if(basicProperties != null){
            message.setBasicProperties(basicProperties);
        }
        //UUID 的生成
        message.basicProperties.setMessageId("M-"+ UUID.randomUUID());
        message.basicProperties.setRoutingKey(routingKey);
        message.setBody(body);
        //只设置两个属性,一个body ,一个 basicProperties
       return message;
    }
}
实现信息属性类

里面包含了信息ID、消息匹配令牌和持久化数据,注意因为是由message引用所有也需要添加Serializable接口。

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class BasicProperties implements Serializable {
    //为了保障消息的唯一性
    private String messageId;
    //用于匹配政策
    private String routingKey;
    //持久化设置 1-》不持久化   2-》持久化
    private int deliverMode = 1;
}

Sqlite中建表

Sqlite中建表,在资源文件中创建一个mateMapper.xml文件用编写创建表格的代码或者修改表格的语句。

这里是将每一个建表语句都分开使用update来建表,能否改为一个update来创建多个表?借助一个方法创建多个表创建?

答:不可以,原因是因为 SQLite 和 MySQL是不一样的,只能执行第一条语句,后续语句直接忽略,所以每执行一次,都需要额外再写一次SQL语句。

SQL建表语句

根据字段进行创建,以下分别是交换机、队列和绑定表的创建。

<update id="createExchangeTable">
    create table if not exists exchange(
    name varchar(50) primary key,
    type int,
    durable boolean,
    autoDelete boolean,
    argument varchar(1024),
    )
</update>
<update id="createQueueTable">
    create table if not exists queue(
    name varchar(50) primary key,
    durable boolean,
    exclusive boolean,
    AutoDelete boolean,
    argument varchar(1024)
    )
</update>
<update id="createBindingTable">
    create table if not exists binding(
    exchangeName varchar(50),
    queueName varchar(50),
    bindingKey varchar(256)
    )
</update>
实现接口调用建表操作

当添加了sql语句后,我们应该在代码实现一个接口来调用文件中的方法。

创建一个专门存放映射接口的文件夹叫mapper,创建一个mateMapper类作为映射关系来调用MateMapper.xml中的方法。

import com.example.mq.mqserver.core.Binding;
import com.example.mq.mqserver.core.Exchange;
import com.example.mq.mqserver.core.MSGQueue;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;
import java.util.concurrent.Executors;

@Mapper
public interface MateMapper {
    //创建三个表
    void createExchangeTable();
    void createQueueTable();
    void createBindingTable();
}
Map数据结构的序列化

Mybatis在操作数据库时,读取数据库数据时会调用Getter方法,我们只需要在这个读取数据的过程中,使得数据是字符串即可,同样的,在设置这个数据时,会调用Setter方法,以同样的方法去修改即可,总而言之在设置Map时将传递数据的格式改为字符串,修改类中的getter和setter方法。

将map参数转换为字符串存储,再将字符串数据转化为Json数据格式使用读取。修改getter方法和setter方法。将序列化代码添加到Exchange、MSGQueue等存在 Map 数据结构的类中。

  1. 这里需要使用到ObjectMapper所以需要添加依赖
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>
  1. 然后使用ObjectMapper中转换字符串的方法。相对于一个序列化和反序列化的过程。
    public String getArguments() {
        //将当前的argument参数修改成String(Json)
        ObjectMapper objectMapper =new ObjectMapper();
        try {
            //转化为json
            return objectMapper.writeValueAsString(arguments);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        //如果出现异常,则返回一个空字符串即可
        return "{}";
    }
    //这个方法,是从数据库中读取数据后,构造Exchange对象,会自动调用的方法
    public void setArguments(String argumentsJson) {
        //把参数中的argumentJson解析,转换成一个map对象
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            objectMapper.readValue(argumentsJson, new TypeReference<HashMap< String, Object >>() {
            });
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

new TypeReference<HashMap< String, Object >>() {}参数含义:

用于描述当前的json字符串转化为字符串类型,使用方法:

  • 简单类型:直接使用对应类型即可
  • 集合这种的复杂类型:使用TypeReference匿名内部类来表示复杂类型的具体信息。

添加增加和删除方法并实现SQL语句。

TypeReference类的意义:

是Java的一种泛型类,主要用于获取泛型中具体类型的信息,并在需要时恢复泛型的信息。

创建数据库和连接数据库

这里提供了两种方法来连接数据库。这里我将数据库名称取名为mate.db

编写一个yml文件:

spring:
  datasource:
    url: jdbc:sqlite:./MessageData/meta.db #相对路径的写法
    username:
    password:
    driver-class-name: org.sqlite.JDBC
  # sqlite不需要写用户名和密码,严密性没有MySql强

编写一个properties文件

#连接 sqlite 数据库
spring.datasource.url=jdbc:sqlite:./data/mate.db
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=org.sqlite.JDBC
#这里不需要填写用户名和密码,因为是单用户数据库

#设置mybatis的接口文件
mybatis.mapper-locations=classpath:mapper/**Mapper.xml

可以随意使用,所以这里两种文件的区别也可以一眼看出。

添加数据

在之前的代码中,进行了建表操作,接下来我们对接口中数据进行一些功能添加。

  • 添加一些初始化数据
  • 在程序启动时,做出逻辑判断
    • 数据库存在,无操作动作
    • 数据库不存在,创建库创建表(如何判断?查询数据库mate.db文件存在性即可)
添加接口方法操作数据库

首先是对表中添加数据操作,可以先在接口中实现API,然后再添加sql语句。

    //添加数据操作
    void addExchange(Exchange exchange);
    void addQueue(MSGQueue queue);
    void addBinding(Binding binding);
	//删除数据操作
	void deleteExchange(String exchangeName);//交换机根据交换机名称删除
    void deleteQueue(String queueName);//队列根据队列名称删除
    void deleteBinding(String ExchangeName, String queueName);//绑定根据交换机和队列的名称删除
//查询操作分为查询一个或者查询所有
    //先实现查询所有数据
    List<Exchange> selectAllExchange();
    List<MSGQueue> selectAllQueue();
    List<Binding> selectAllBinding();
    //查询一个
    Exchange selectExchange(String exchangeName);
    MSGQueue selectQueue(String queueName);
    Binding selectBinding(Binding binding);
MateMapper.xml中实现操作sqlite语句

Mybatis字段名含义

  1. id:方法名称对应到接口中的方法名
  2. parameterType:对调用mapper接口的使用的参数类型
  3. resultType:查询结果的参数类型
  1. 添加数据
  2. 删除数据
  3. 查找数据
<!-- 添加数据 -->
    <insert id="addExchange" parameterType="com.example.mq.mqserver.core.Exchange">
        insert into exchange value(#{name},#{type},#{durable},#{autoDelete},#{argument})
    </insert>
    <insert id="addQueue" parameterType="com.example.mq.mqserver.core.MSGQueue">
        insert into queue value(#{name},#{durable},#{exclusive},#{autoDelete},#{argument})
    </insert>
    <insert id="addBinding" parameterType="com.example.mq.mqserver.core.Binding">
        insert into binding value(#{exchangeName},#{queueName),#{BindingKey});
    </insert>
    <!--删除数据-->
    <delete id="deleteExchange" parameterType="com.example.mq.mqserver.core.Exchange">
        delete from exchange where name = #{exchangeName};
    </delete>
    <delete id="deleteQueue" parameterType="com.example.mq.mqserver.core.MSGQueue">
        delete from queue where name = #{queueName};
    </delete>
<delete id="deleteBinding" parameterType="com.example.mq.mqserver.core.Binding">
        delete from binding where exchangeName = #{exchangeName} and queueName = #{queueName};
    </delete>
    <!--查找数据-->
    <select id="selectAllExchange" resultType="com.example.mq.mqserver.core.Exchange">
        select * from exchange
    </select>
    <select id="selectAllQueue" resultType="com.example.mq.mqserver.core.MSGQueue">
        select * from queue;
    </select>
    <select id="selectAllBinding" resultType="com.example.mq.mqserver.core.Binding">
        select * from binding
    </select>
    <select id="selectExchange" resultType="com.example.mq.mqserver.core.Exchange">
        select * from exchange where name = #{exchangeName};
    </select>
    <select id="selectQueue" resultType="com.example.mq.mqserver.core.MSGQueue">
        select * from queue where name = #{queueNAme};
    </select>
    <select id="selectBinding" resultType="com.example.mq.mqserver.core.Binding">
        select * from binding where exchangeName = #{exchangeName} and queueName = #{queueName};
    </select>
使用DataBaseManger总和

以上就是我们第一步的创建工作,接下来使用DataBaseManger来总和上述功能。

mqserver中创建一个文件夹名为datacenter(数据中心),文件夹创建一个类,类名为DataBaseManger,然后使用这个类来调用我们刚刚创建的方法,创建数据库文件、删除数据库文件和建表等操作。

整合步骤

  1. 创建一个 MateMapper
  2. 创建初始化方法,
    1. 检查数据库可靠性,不存在就创建一个,
    2. 添加默认交换机
    3. 添加数据库文件
    4. 建表操作
  3. 删除数据库文件(这里为了方便后面代码的添加,提前实现)
import com.example.mq.MqApplication;
import com.example.mq.mqserver.mapper.MateMapper;

import java.io.File;

public class DataBaseManger {
    private MateMapper mateMapper;

    //初始化
    public void init(){
        //获取bean 对象
        mateMapper = MqApplication.context.getBean(MateMapper.class);
        //检查数据库的存在性
        if(! checkDBExists()){
            //数据库不存在,进行以下操作
            // 1. 创建目录
            File dataFile = new File("./data");
            dataFile.mkdirs();
            // 2. 创建数据库
            cretaeTable();//将三个表对封装在里面
            // 3. 插入默认数据
            creataDefaultData();//添加一个默认的交换机,这是因为 RabbitMq中有一个
            System.out.println("[DataBAseManger]第一阶段 数据库初始化完成!");
        }else {
            System.out.println("[DataBaseManger]第一阶段 数据库已经存在!");
        }
    }

    private boolean checkDBExists() {
        File file = new File("/data/mate.db");
        return file.exists();
    }
    //删除数据库文件
    public void deleteDataBase(){
        //1. 删除文件
        //2. 删除目录
        File file = new File("./data/mte.db");
        boolean delete = file.delete();
        if(delete){
            System.out.println("[DataBaseManger] 删除数据库成功!");
        }else {
            System.out.println("[DataBaseManger] 删除数据库失败!");
        }
        File deleteDir = new File("./data");
        delete = deleteDir.delete();
        if(delete){
            System.out.println("[DataBaseMAnger] 删除数据库目录成功!");
        }else {
            System.out.println("[DataBaseMAnger] 删除数据库目录失败!");
        }
    }
}

这里我们添加两个方法给上面代码使用

创建表工作:

private void createTable() {
    mateMapper.createExchangeTable();
    mateMapper.createQueueTable();
    mateMapper.createExchangeTable();
    System.out.println("[DataBaseMange]第一阶段 建表完成!");
}

添加默认交换机数据

private void createDefaultData() {
    Exchange exchange = new Exchange();
    exchange.setName("");
    exchange.setType(ExchangeType.DIRECT);
    exchange.setDurable(false);
    exchange.setAutoDelete(false);
    System.out.println("[DataBaseManger] 第一阶段 添加默认数据成功 ");
}

最后将系统中的后续添加的方法写入代码中。

//添加程序后续方法
    public void addExchange(Exchange exchange){
        mateMapper.addExchange(exchange);
    }
    public void addQueue(MSGQueue queue){
        mateMapper.addQueue(queue);
    }
    public void addBinding(Binding binding){
        mateMapper.addBinding(binding);
    }
    public void deleteExchange(String exchangeName){
        mateMapper.deleteExchange(exchangeName);
    }
    public void deleteQueue(String queueName){
        mateMapper.deleteQueue(queueName);
    }
    public void deleteBinding(Binding binding){
        mateMapper.deleteBinding(binding);
    }
    public List<Exchange> selectAllExchange(){
        return mateMapper.selectAllExchange();
    }
    public List<MSGQueue> selectAllQueue(){
        return mateMapper.selectAllQueue();
    }
    public List<Binding> selectAllBinding(){
        return mateMapper.selectAllBinding();
    }
    public Exchange selectExchange(String exchangeName){
        return mateMapper.selectExchange(exchangeName);
    }
    public MSGQueue selectQueue(String queueName){
        return mateMapper.selectQueue(queueName);
    }
    public Binding selectBinding(Binding binding){
        return mateMapper.selectBinding(binding);
    }

接下来我们将对这个整合类进行单元测试。

单元测试

注释解读:

@BeforEach:每执行一个测试方法之前执行一次(一般用于初始化一个对象)

@AfterEach:每执行完一个测试方法后执行一次(一般用于关闭资源)

针对DataBaseMAnger进行一次单元测试。

单元测试用例和用例是互不干扰的,相互独立。

测试目标:

  1. 将三个模块的增删改查进行测试和修改
  2. 达到预期目标

首先我们创建一个 dataBaseManger类以便后续调用,添加两个方法来创建数据库对象和删除数据库对象。

import com.example.mq.mqserver.datacenter.DataBaseManger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.boot.SpringApplication;
@SpringBootTest
public class DataBaseMangerTest {
    private DataBaseManger dataBaseManger;
    @BeforeEach
    public void setUp(){
        //通过 SpringApplication 得到 mataMapper 实例
        MqApplication.context = SpringApplication.run(MqApplication.class);
        //初始化dataBaseManger对象  
        dataBaseManger.init();
    }
    @AfterEach
    public void tearDown(){
        //1.关闭 context 对象
        //2. 删除数据库文件
        MqApplication.context.close();
        dataBaseManger.deleteDataBase();
    }
}
测试初始化

在使用了初始化方法后,我们需要测试在初始化方法中的建表功能和添加默认数据是否成功。

@Test
public void testInit(){
    //检查数据库状态,查询数据库中存在该表
    List<Exchange> exchanges = dataBaseManger.selectAllExchange();
    List<MSGQueue> msgQueues = dataBaseManger.selectAllQueue();
    List<Binding> bindings = dataBaseManger.selectAllBinding();
    //使用断言来判断表中数据
    Assertions.assertEquals(1,exchanges.size());
    //如果查询到存在数据,需要判断数据是不是我们默认添加的那一条
    Assertions.assertEquals("",exchanges.get(0).getName());
    Assertions.assertEquals(ExchangeType.DIRECT,exchanges.get(0).getType());
    Assertions.assertTrue(exchanges.get(0).isDurable());
    Assertions.assertEquals(0,msgQueues.size());
    Assertions.assertEquals(0,bindings.size());
    }

结果:

在这里插入图片描述

创建与删除交换机
  1. 设置一个方法对一个交换机进行实例化
  2. 编写测试代码
  3. 将刚刚的交换机引入,然后进行添加到数据库操作
  4. 查询数据库中交换机是否和本地交换机相同
  5. 删除交换机,再次查询数据库中的交换机,是否为空

代码实现:

 public Exchange createExchangeTest(String exchangeName){
        Exchange exchange = new Exchange();
        exchange.setName(exchangeName);
        exchange.setType(ExchangeType.DIRECT);
        exchange.setDurable(true);
        exchange.setAutoDelete(false);

        return exchange;
    }

    @Test
    public void testExchange(){
        //1.构造一个交换机并插入
        Exchange expectExchange = createExchangeTest(exchangeName);
        dataBaseManger.addExchange(expectExchange);
        //2.查询这个交换机,比较结果比较是否一致
        Exchange actualExchange = dataBaseManger.selectExchange(exchangeName);
        Assertions.assertEquals(expectExchange,actualExchange);
        //3.删除这个交换机
        dataBaseManger.deleteExchange(exchangeName);
        //4.测试交换机是否存在
        actualExchange = dataBaseManger.selectExchange(exchangeName);
        Assertions.assertNull(actualExchange);
    }

在这里插入图片描述

创建队列

代码实现:

public MSGQueue createQueueTest(String queueName){
    MSGQueue msgQueue = new MSGQueue();
    msgQueue.setName(queueName);
    msgQueue.setDurable(true);
    msgQueue.setExclusive(false);
    msgQueue.setAutoDelete(false);
    return msgQueue;
}
@Test
public void testQueue(){
    //1. 创建一个队列并添加到数据库中
    MSGQueue expectQueue = createQueueTest(queueName);
    dataBaseManger.addQueue(expectQueue);
    //2.查询数据库中姓名为 queueName 的队列
    MSGQueue actualQueue = dataBaseManger.selectQueue(queueName);
    //3.创建的队列和数据库中进行对比
    Assertions.assertEquals(expectQueue,actualQueue);
    //4.删除这个队列
    dataBaseManger.deleteQueue(queueName);
    //5.再次查询同样队列名称,是否为 null
    actualQueue = dataBaseManger.selectQueue(queueName);
    Assertions.assertNull(actualQueue);
}

在这里插入图片描述

创建绑定

代码实现:

//创建绑定需要一个交换机和一个队列
public Binding creatTestBinding(String exchangeName,String queueName){
    Binding binding = new Binding();
    binding.setExchangeName(exchangeName);
    binding.setQueueName(queueName);
    binding.setBindingKey("testBindKeys");
    return binding;
}
@Test
public void bindTest(){
    //1.添加绑定到数据库中
    Binding expectBind = creatTestBinding(exchangeName, queueName);
    dataBaseManger.addBinding(expectBind);
    //2. 查询绑定数据然后对比数据
    Binding actualBind = dataBaseManger.selectBinding(expectBind);
    //3. 对比数据是否相同
    Assertions.assertEquals(expectBind,actualBind);
    //4. 删除绑定
    dataBaseManger.deleteBinding(actualBind);
    //5.查询绑定并比较数据
    actualBind = dataBaseManger.selectBinding(expectBind);
    Assertions.assertNull(actualBind);
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值