目录
[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());
}