Java自定义协议的发布订阅式消息队列(二)

目录

一、数据库设计

[MetaMapper 接口:](#MetaMapper 接口:)

[MetaMapper.xml 文件:](#MetaMapper.xml 文件:)

[实现 DataBaseManager](#实现 DataBaseManager)

[二、测试 DataBaseManager:](#二、测试 DataBaseManager:)

[测试数据库初始化 testIntiTable](#测试数据库初始化 testIntiTable)

[测试插入交换机 testInsertExchange](#测试插入交换机 testInsertExchange)

[测试删除交换机 testDeleteExchange](#测试删除交换机 testDeleteExchange)

[测试插入队列 testInsertQueue](#测试插入队列 testInsertQueue)

[测试删除队列 testDeleteQueue](#测试删除队列 testDeleteQueue)

[测试插入绑定 testInsertBinding](#测试插入绑定 testInsertBinding)

[测试删除绑定 testDeleteBinding](#测试删除绑定 testDeleteBinding)


一、数据库设计

对于 Exchange、MSGQueue、Binding,我们使用数据库进行持久化保存。

此处我们使用的数据库是 SQLite,是一个更轻量的数据库。

SQLite 只是一个动态库(当然,官方也提供了可执行程序 exe),我们在 Java 中直接引入 SQLite 依赖,即可直接使用,不必安装其他的软件。

引入 pom.xml 依赖:

XML 复制代码
<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.41.0.1</version>
</dependency>

配置数据源 application.yml:

XML 复制代码
spring:
  datasource:
    url: jdbc:sqlite:/Users/aa/Documents/Java代码/发布订阅式消息队列/mq/meta.db
    username:
    password:
    driver-class-name: org.sqlite.JDBC

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

Username 和 password 空着即可。

此处我们约定,把数据库文件放到./data/meta.db 中。

SQLite 只是把数据单纯的存储到一个文件中。非常简单方便。

MetaMapper 接口:

java 复制代码
package com.example.mq.mqserver.mapper;

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;

@Mapper
public interface MetaMapper {
    //操作交换机
    void createExchangeTable();
    void insertExchange(Exchange exchange);
    void deleteExchange(String exchangeName);
    List<Exchange> selectAllExchanges();

    //操作队列
    void createQueueTable();
    void insertQueue(MSGQueue queue);
    void deleteQueue(String queueName);
    List<MSGQueue> selectAllQueues();
    //操作绑定
    void createBindingTable();
    void insertBinding(Binding binding);
    void deleteBinding(Binding binding);
    List<Binding> selectAllBindings();
}

MetaMapper.xml 文件:

建表操作使用 update 标签实现。

java 复制代码
<?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.mq.mqserver.mapper.MetaMapper">

<!--对交换机的操作-->
    <update id="createExchangeTable">
        create table if not exists exchange(
        name varchar(50) primary key,
        type int,
        durable boolean,
        autoDelete boolean,
        arguments varchar(1024)
        );
    </update>
    <select id="selectAllExchanges" resultType="com.example.mq.mqserver.core.Exchange">
        select * from exchange
    </select>
    <insert id="insertExchange" parameterType="com.example.mq.mqserver.core.Exchange">
        insert into exchange values(#{name}, #{type}, #{durable}, #{autoDelete}, #{arguments});
    </insert>
    <delete id="deleteExchange" parameterType="java.lang.String">
        delete from exchange where name = #{exchangeName};
    </delete>

<!--对队列的操作-->
    <update id="createQueueTable">
        create table if not exists queue(
        name String,
        durable boolean,
        exclusive boolean,
        autoDelete boolean,
        arguments varchar(1024)
        );
    </update>
    <select id="selectAllQueues" resultType="com.example.mq.mqserver.core.MSGQueue">
        select * from queue
    </select>
    <insert id="insertQueue" parameterType="com.example.mq.mqserver.core.MSGQueue">
        insert into queue values(#{name}, #{durable}, #{exclusive}, #{autoDelete}, #{arguments});
    </insert>
    <delete id="deleteQueue" parameterType="java.lang.String">
        delete from queue where name = #{queueName};
    </delete>

<!--对绑定的操作-->
    <update id="createBindingTable">
        create table if not exists binding(
        exchangeName varchar(50),
        queueName varchar(50),
        bindingKey varchar(256)
        );
    </update>
    <select id="selectAllBindings" resultType="com.example.mq.mqserver.core.Binding">
        select * from binding
    </select>
    <insert id="insertBinding" parameterType="com.example.mq.mqserver.core.Binding">
        insert into binding values(#{exchangeName}, #{queueName}, #{bindingKey});
    </insert>
    <delete id="deleteBinding" parameterType="com.example.mq.mqserver.core.Binding">
        delete from binding where exchangeName = #{exchangeName} and queueName = #{queueName};
    </delete>
</mapper>

实现 DataBaseManager

通过这个类来整合上述的数据库操作,用来调用MetaMapper中的方法。

java 复制代码
package com.example.mq.mqserver.datacenter;

import com.example.mq.DemoApplication;
import com.example.mq.mqserver.core.Binding;
import com.example.mq.mqserver.core.Exchange;
import com.example.mq.mqserver.core.ExchangeType;
import com.example.mq.mqserver.core.MSGQueue;
import com.example.mq.mqserver.mapper.MetaMapper;

import java.io.File;
import java.util.List;

//通过这个类来整合上述的数据库操作
public class DataBaseManager {
    //这里不使用@Autowried注解,手动获取MetaMapper
    private MetaMapper metaMapper;
    //针对数据库进行初始化操作
    public void inti(){
        //启动类类中创建上下文对象,来手动获取到MetaMapper
        metaMapper= DemoApplication.context.getBean(MetaMapper.class);

        if(!checkDBExists()){
            //如果数据库不存在,则创建库和数据表
            createTable();
            //插入默认数据
            createDefaultData();
            System.out.println("[DataBaseManager] 数据库初始化完成!");
        }else{
            System.out.println("[DataBaseManager] 数据库已经存在!");
        }
    }
    //删除数据库文件
    public void deleteDB(){
        File file=new File("/Users/aa/Documents/Java代码/发布订阅式消息队列/mq/meta.db");
        boolean ret=file.delete();
        if(ret){
            System.out.println("[DataBaseManager] 删除数据库成功!");
        }else{
            System.out.println("[DataBaseManager] 删除数据库失败!");
        }
    }

    public void insertExchange(Exchange exchange) {
        metaMapper.insertExchange(exchange);
    }

    public void deleteExchange(String exchangeName) {
        metaMapper.deleteExchange(exchangeName);
    }

    public List<Exchange> selectAllExchanges() {
        return metaMapper.selectAllExchanges();
    }

    public void insertQueue(MSGQueue queue) {
        metaMapper.insertQueue(queue);
    }

    public void deleteQueue(String queueName) {
        metaMapper.deleteQueue(queueName);
    }

    public List<MSGQueue> selectAllQueues() {
        return metaMapper.selectAllQueues();
    }

    public void insertBinding(Binding binding) {
        metaMapper.insertBinding(binding);
    }

    public void deleteBinding(Binding binding) {
        metaMapper.deleteBinding(binding);
    }

    public List<Binding> selectAllBindings() {
        return metaMapper.selectAllBindings();
    }
    //判断数据库是否存在,就是查找是否存在./data/meta.db这个文件
    private boolean checkDBExists() {
        File file=new File("./data/meta.db");
        if(file.exists()){
            return true;
        }
        return false;
    }
    //使用这个操作来建表,不需要手动创建./data/meta.db文件
    //首次执行这里的数据库操作时,会自动创建出./data/meta.db文件
    private void createTable() {
        metaMapper.createExchangeTable();
        metaMapper.createQueueTable();
        metaMapper.createBindingTable();
        System.out.println("[DataBaseManager] 创建表完成!");
    }
    //给刚创建的数据表中,添加默认的数据
    //这里这要是添加一个默认的交换机
    //因为RabbitMQ中,就会有一个默认的交换机,类型是DIRECT
    private void createDefaultData() {
        //构造一个默认的交换机
        Exchange exchange=new Exchange();
        //设置交换机的属性
        exchange.setName("");
        exchange.setType(ExchangeType.DIRECT);
        exchange.setDurable(true);
        exchange.setAutoDelete(false);
        //将交换机插入数据表中
        metaMapper.insertExchange(exchange);
        System.out.println("[DataBaseManager] 创建初始数据完成!");
    }

}

上述代码中的 MetaMapper 没有交给 Spring 来管理,则必须要从 Spring 上下文中拿到 MetaMapper 这个对象,否则会出现空指针异常。

所以在 DemoApplication 中创建 ConfigurableApplicationContext(Spring 上下文)对象来获取 bean。

java 复制代码
package com.example.mq;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import java.util.Stack;

@SpringBootApplication
public class DemoApplication {
	public static ConfigurableApplicationContext context;
	public static void main(String[] args) {

		context=SpringApplication.run(DemoApplication.class, args);
	}

}

二、单元测试 DataBaseManager:

在 test 目录中,创建 DataBaseManagerTests 测试类。

设计单元测试,要保证用例和用例之间是相互独立,互不影响的。

所以我们在每执行一个用例之前,先搭建测试环境、准备好测试数据。

每执行一个用例之后,将这个用例产生的影响和作用给消除掉。

testInitTable 方法中的 Assertions 是断言类,一旦有方法返回 false,则会终止这个测试方法。

java 复制代码
package com.example.mq.mqserver.datacenter;

import com.example.mq.DemoApplication;
import com.example.mq.mqserver.core.Binding;
import com.example.mq.mqserver.core.Exchange;
import com.example.mq.mqserver.core.ExchangeType;
import com.example.mq.mqserver.core.MSGQueue;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class DataBaseManagerTest {
    private DataBaseManager dataBaseManager=new DataBaseManager();
    //每一个测试用例之前的准备工作,执行的时数据库初始化
    @BeforeEach
    public void setup(){
        //由于在使用 DataBaseManager 中的 inti 方法时,会用到 MetaMapper 的实例
        //所以要先得到 context 对象,再手动获取到 MetaMapper 实例
        DemoApplication.context= SpringApplication.run(DemoApplication.class);
        dataBaseManager.inti();
    }
    //每一个测试用例之后的收尾工作,用来清空数据库
    @AfterEach
    public void tearDown(){
        //注意:此处不能直接删除数据库文件,应该先关闭上述的 context 对象
        //此处的 context 对象,持有了 MetaMapper 的实例,MetaMapper 实例又打开了 meta.db 数据库文件
        //当文件已经被打开时,此时删除文件是不会成功的
        DemoApplication.context.close();
        dataBaseManager.deleteDB();
    }
    
}

测试数据库初始化 testIntiTable

java 复制代码
@Test
    public void testIntiTable(){
        //此时的 inti 方法在之前已经被调用过,所以只需要直接检查数据库状态即可
        //看交换机表中,是否存在默认的数据即可(是否有匿名交换机)
        List<Exchange> exchangeList = dataBaseManager.selectAllExchanges();
        List<MSGQueue> msgQueueList = dataBaseManager.selectAllQueues();
        List<Binding> bindingList = dataBaseManager.selectAllBindings();
        //可以通过打印来判断结果,但是不方便
        //使用断言,这个方法是判断两个值是否相等,
        //方法中第一个参数为预期参数,第二个为实际参数
        Assertions.assertEquals(1,exchangeList.size());
        Assertions.assertEquals("",exchangeList.get(0).getName());
        Assertions.assertEquals(ExchangeType.DIRECT,exchangeList.get(0).getType());
        Assertions.assertEquals(0,msgQueueList.size());
        Assertions.assertEquals(0,bindingList.size());
    }

测试插入交换机 testInsertExchange

java 复制代码
//构建exchange 对象
    private Exchange createExchange(String exchangeName){
        Exchange exchange=new Exchange();
        exchange.setName(exchangeName);
        exchange.setType(ExchangeType.FANOUT);
        exchange.setAutoDelete(false);
        exchange.setDurable(true);
        exchange.setArguments("aaa",1);
        exchange.setArguments("bbb",2);
        return exchange;
    }
@Test
    public void testInsertExchange(){
        //先构造出一个 Exchange 对象,插入到数据库中,再查询出来,看看结果是否符合预期
        Exchange exchange=createExchange("testExchange");
        dataBaseManager.insertExchange(exchange);
        //插入完成后,查询结果
        List<Exchange> exchangeList=dataBaseManager.selectAllExchanges();
        //两个 exchange 对象,一个是默认的,一个是才创建好的
        Assertions.assertEquals(2,exchangeList.size());
        //再拿出刚刚插入的那个exchange对象,查看它的属性是否正确
        Exchange newExchange=exchangeList.get(1);
        Assertions.assertEquals("testExchange",newExchange.getName());
        Assertions.assertEquals(ExchangeType.FANOUT,newExchange.getType());
        Assertions.assertEquals(false,newExchange.isAutoDelete());
        Assertions.assertEquals(true,newExchange.isDurable());
        Assertions.assertEquals(1,newExchange.getArguments("aaa"));
        Assertions.assertEquals(2,newExchange.getArguments("bbb"));
    }

测试删除交换机 testDeleteExchange

java 复制代码
@Test
    public void testDeleteExchange(){
        //先构建一个 exchange 对象,插入数据库,然后按名字删除数据库
        Exchange exchange=createExchange("testExchange");
        dataBaseManager.insertExchange(exchange);
        //插入完成后,查询结果
        List<Exchange> exchangeList=dataBaseManager.selectAllExchanges();
        //两个 exchange 对象,一个是默认的,一个是才创建好的
        Assertions.assertEquals(2,exchangeList.size());
        Assertions.assertEquals("testExchange",exchangeList.get(1).getName());

        //进行删除操作
        dataBaseManager.deleteExchange("testExchange");
        //再次查询
        exchangeList=dataBaseManager.selectAllExchanges();
        Assertions.assertEquals(1,exchangeList.size());
        Assertions.assertEquals("",exchangeList.get(0).getName());
    }

测试插入队列 testInsertQueue

java 复制代码
//创建 MSGQueue 对象
    private MSGQueue createMSGQueue(String queueName){
        MSGQueue msgQueue=new MSGQueue();
        msgQueue.setName(queueName);
        msgQueue.setAutoDelete(false);
        msgQueue.setExclusive(false);
        msgQueue.setDurable(true);
        msgQueue.setArguments("aaa",1);
        msgQueue.setArguments("bbb",2);
        return msgQueue;
    }
    @Test
    public void testInsertQueue() {
        //先创建
        MSGQueue msgQueue=createMSGQueue("testQueue");
        dataBaseManager.insertQueue(msgQueue);
        //插入完成后,查询结果
        List<MSGQueue> msgQueueList=dataBaseManager.selectAllQueues();
        Assertions.assertEquals(1,msgQueueList.size());
        //再拿出刚刚插入的那个MSGQueue对象,查看它的属性是否正确
        MSGQueue newMSGQueue=msgQueueList.get(0);
        Assertions.assertEquals("testQueue",newMSGQueue.getName());
        Assertions.assertEquals(false,newMSGQueue.isExclusive());
        Assertions.assertEquals(false,newMSGQueue.isAutoDelete());
        Assertions.assertEquals(true,newMSGQueue.isDurable());
        Assertions.assertEquals(1,newMSGQueue.getArguments("aaa"));
        Assertions.assertEquals(2,newMSGQueue.getArguments("bbb"));
    }

测试删除队列 testDeleteQueue

java 复制代码
@Test
    public void testDeleteQueue(){
        //先创建
        MSGQueue msgQueue=createMSGQueue("testQueue");
        dataBaseManager.insertQueue(msgQueue);
        //插入完成后,看看是否插入成功
        List<MSGQueue> msgQueueList=dataBaseManager.selectAllQueues();
        Assertions.assertEquals(1,msgQueueList.size());
        //开始删除
        dataBaseManager.deleteQueue("testQueue");
        msgQueueList=dataBaseManager.selectAllQueues();
        Assertions.assertEquals(0,msgQueueList.size());
    }

测试插入绑定 testInsertBinding

java 复制代码
 @Test
    public void testInsertBinding() {
        Binding binding = new Binding();
        binding.setQueueName("testQueue");
        binding.setExchangeName("testExchange");
        binding.setBindingKey("testBindingKey");
        dataBaseManager.insertBinding(binding);

        List<Binding> bindingList = dataBaseManager.selectAllBindings();
        Assertions.assertEquals(1, bindingList.size());
        Assertions.assertEquals("testQueue", bindingList.get(0).getQueueName());
        Assertions.assertEquals("testExchange", bindingList.get(0).getExchangeName());
        Assertions.assertEquals("testBindingKey", bindingList.get(0).getBindingKey());
    }

测试删除绑定 testDeleteBinding

java 复制代码
@Test
    public void testDeleteBinding() {
        Binding binding = new Binding();
        binding.setQueueName("testQueue");
        binding.setExchangeName("testExchange");
        binding.setBindingKey("testBindingKey");
        dataBaseManager.insertBinding(binding);

        List<Binding> bindingList = dataBaseManager.selectAllBindings();
        Assertions.assertEquals(1, bindingList.size());

        dataBaseManager.deleteBinding(binding);

        bindingList = dataBaseManager.selectAllBindings();
        Assertions.assertEquals(0, bindingList.size());
    }
相关推荐
雨中飘荡的记忆1 小时前
拼团系统设计与实现
java·spring boot
青云交1 小时前
Java 大视界 -- Java 大数据在智能医疗影像数据标注与疾病辅助诊断模型训练中的应用
java·大数据·多模态融合·医疗影像标注·辅助诊断·临床 ai·dicom 处理
雨中飘荡的记忆1 小时前
Step Builder模式实战
java·设计模式
悦来客栈的老板1 小时前
AST反混淆实战|reese84_jsvmp反编译前的优化处理
java·前端·javascript·数据库·算法
悟空码字1 小时前
SpringBoot实现日志系统,Bug现形记
java·spring boot·后端
狂奔小菜鸡1 小时前
Day24 | Java泛型通配符与边界解析
java·后端·java ee
车软派开发学长1 小时前
车软嵌入式AUTOSAR学习教材推荐
学习
IMPYLH1 小时前
Lua 的 tostring 函数
开发语言·笔记·junit·单元测试·lua
c_h_o_i_c_e1 小时前
MATLAB 帮助文档设置内置浏览器【Web浏览器/内置浏览器】
开发语言·matlab