项目实战 — 消息队列(2){数据库操作}

目录

一、SQLite

[🍅 1、添加依赖](#🍅 1、添加依赖)

[🍅 2、修改配置文件后缀(properties -> yaml)](#🍅 2、修改配置文件后缀(properties -> yaml))

[🍅 3、编写配置文件](#🍅 3、编写配置文件)

二、建立数据表

三、添加插入和删除方法

四、整合数据库操作(DataBaseManger类)

[🍅 1、初始化方法init()](#🍅 1、初始化方法init())

[🍅 2、编写代码](#🍅 2、编写代码)

五、对数据库操作进行单元测试

[🍅 1、"准备工作"和"收尾工作"](#🍅 1、“准备工作”和“收尾工作”)

[🍅 2、编写测试类进行用力测试](#🍅 2、编写测试类进行用力测试)

🎈测试init()方法

[🎈测试 交换机(插入和删除)](#🎈测试 交换机(插入和删除))

[* Delete](#* Delete)

[🎈 测试DataBaseManager的队列(插入和删除)](#🎈 测试DataBaseManager的队列(插入和删除))

🎈测试Binding

六、小结

[🍅 1、运行时可能会报错](#🍅 1、运行时可能会报错)

[🍅 2、已经完成的任务](#🍅 2、已经完成的任务)


一、SQLite

MySQL数据库本身是比较重量的,所以这里使用SQLite,SQLite是更轻量的数据库

SQLite的优点:

  • 服务器性能和内存要求低
  • 减少了能源消耗
  • 自成一体,便于携带
  • 默认包含在所有的PHP安装中

它是一个本地的数据库, 操作该数据库就相当与直接操作本地的硬盘文件。

SQLite应用是很广泛的,在一些性能不高的设备上,比如移动端和嵌入式设备,就可以使用SQLite。

而且,也可以通过mybatis来使用。这里创建一个mapper文件夹将有关mybatis的xml文件放到其中。

🍅 1、添加依赖

使用过SQLite不用额外安装,直接引入依赖即可。

XML 复制代码
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.41.0.0</version>
</dependency>

引入以后,reload。

🍅 2、修改配置文件后缀(properties -> yaml)

这里的配置文件主要使用yaml格式。

🍅 3、编写配置文件

对于SQLite文件来说,不需要指定用户名和密码,原因如下:

首先,MySQL是一个客户端服务器结构的程序,一个数据库服务器,就会对应有很多个客户端来访问;

但是,SQLite不同,它不是一个客户端服务器结构的程序,只有自己一个人能够访问,把数据放在本地文件上面,只有当前主机才能访问,和网络无关。

bash 复制代码
spring:
  datasource:
#    SQLite数据库是将数据存储在当前硬盘的某个指定的为文件中
#    这是一个相对路径,运行以后,这个文件就会出现在当前项目的目录中
    url: jdbc:sqlite:./data/meta.db
#    SQLite并不需要指定用户名和密码
    username:
    password:
    driver-class-name: org.sqlite.JDBC
  mybatis:
    mapper-location: classpath:mapper/**Mapper

二、建立数据表

SQLite没有建数据库的这概念,一个.db文件就相当于一个库,程序一启动就会自动建库。

主要建立以下几个表:

* 交换机存储

* 队列存储

* 绑定存储

这里通过代码自动完成建表操作,使用Mybatis执行SQL语句。

MyBatis基本使用流程回顾:

(1)创建一个interface,描述有哪些方法要给java代码使用

(2)创建对应的xml,通过xml实现interface中的抽象方法

创建一个mapper包,放置interface。

在MeteMapper接口中建立三个核心的建表方法:

java 复制代码
@Mapper
public interface MetaMapper {
    //提供三个核心建表方法
    void createExchangeTable();
    void createQueueTable();
    void createBindingTable();
}

然后再Mapper文件夹中建立对应的xml文件,

编写myBatis文件,关于myBatis框架的使用,如果不了解,可以参考博客https://blog.csdn.net/qq_52136076/category_12392841.html?spm=1001.2014.3001.5482

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.tigermq.mqserver.mapper.MetaMapper">
    <update id="createExchangeTable">
        create table if not exists exchange(
            name varchar(50) primary key,
            type int,
            durable boolean,
        )
    </update>

    <update id="createQueueTable">
        create table if not exists queue(
            name varchar(50) primary key,
            durable boolean,
            exclusive boolean,
        )
    </update>

    <update id="createBindingTable">
        create table if not exists binding(
            exchangeName varchar(50),
            queueName varchar(50),
            bindingKey varchar(256)
        )
    </update>

</mapper>

三、添加插入和删除方法

在MentaMapper接口中添加插入和删出方法,其中

对于交换机和队列这两个表,由于使用name作为主键,所以直接按照name进行删除即可。

而对于绑定来说,没有主键,删除操作其实是针对exchangeName和queueName两个维度进行筛选。

java 复制代码
//    针对三个表,进行插入\删除\查找操作
    @Insert("insert into exchange values(#{name},#{type},#{durable})")
    void insertExchange(Exchange exchange);

    @Select("select * from exchange")
    List<Exchange> selectAllExchanges();

    @Delete("delete from exchange where name = #{exchangeName}")
    void deleteExchange(String exchangeName);

    @Insert("insert into queue values (#{name},#{durable},#{exclusive})")
    void insertQueue(MSGQueue queue);

    @Select("select * from queue")
    List<MSGQueue> selectAllQueues();

    @Delete("delete from queue where name = (#{queueName})")
    void deleteQueue(String queueName);

    @Insert("insert into binding values(#{exchangeName},#{queueName},#{bindingKey})")
    void insertBinding(Binding binding);

    @Select("select * from binding")
    List<Binding> selectAllBindings();

    @Delete("delete from binding where exchangeName = #{exchangeName} and queueName = #{queueName}")
    void deleteBinding(Binding binding);

四、整合数据库操作(DataBaseManger类)

创建一个包datacenter .都是针对数据进行操作

在包中创建一个DataBaseManger类

🍅 1、初始化方法init()

首先,由于构造方法初始化一般都不会带有很多逻辑(也可以用,但是一般习惯不适用构造方法),而这里初始化数据库需要很多业务逻辑,所以就自定义了一个init()方法。

数据库的初始化其实就是 建库建表 + 插入一些默认数据

基本逻辑:

如果数据已经存在了,就不做出任何操作

如果数据不存在,则创 建库 + 表 + 构造默认数据

如何判断数据库是否存在?

答:判断meta.db文件是否存在。

🍅 2、编写代码

根据前面的逻辑,编写如下代码:

java 复制代码
/*
* 通过这个类,整合之前的数据库操作
* */
public class DataBaseManger {
//    从spring中拿到现成的对象
    private MetaMapper metaMapper;

//    针对数据库进行初始化
//    因为这里的的初始化需要带有业务逻辑,所以就不适用构造方法,因为构造方法一般不会涉及到很多的业务逻辑
    public void init(){
//          手动的获取到MetaMapper
        metaMapper = TigerMqApplication.context.getBean(MetaMapper.class);

        if(!checkDBExists()){

//            创建一个data目录
            File dataDir = new File("/data");
            dataDir.mkdirs();
//            数据库不存在,就进行建库建表操作
            createTable();
//            创建默认数据
            createDafaultData();
            System.out.println("[DataBaseManger]数据库初始化完成");

        }else{
//            数据库存在了
            System.out.println("[DataBaseManger]数据库已经存在");
        }

    }

    public void deleteDB(){
        File file = new File("./data/meta.db");
        boolean ret = file.delete();
        if (ret){
            System.out.println("[DataBaseManager]DB文件已经删除成功");
        }else{
            System.out.println("[DataBaseManager]DB文件删除失败");
        }


        //这个Delete只能删除空目录,所以删除的时候要保证目录是空的
        File dataDir = new File("./data");
        ret = dataDir.delete();
        if(ret){
            System.out.println("[DataBaseManger] 删除数据库目录成功");
        }else {
            System.out.println("[DataBaseManger] 删除数据库目录失败");
        }
    }

    //    判断文件是否存在
    private boolean checkDBExists() {
        File file = new File("./data/meta.db");
        if (file.exists()){
            return true;
        }
        return false;
    }

//    建表
//    建库操作不需要手动执行
//    首次执行,会自动创建出meta.db文件(mybatis会帮助我们完成)
    private void createTable() {
//        下面这些方法之前已经创建过了
        metaMapper.createExchangeTable();
        metaMapper.createQueueTable();
        metaMapper.createBindingTable();
        System.out.println("[DataBaseManger]创建表完成");
    }

//    创建默认数据
//    此处主要是添加一个默认的交换机:DIRECT
    private void createDafaultData() {
//        构造一个默认的交换机
        Exchange exchange = new Exchange();
        exchange.setName("");
        exchange.setType(ExchangeType.DIRECT);
        exchange.setDurable(true);
        exchange.setAutoDelete(false);
        metaMapper.insertExchange(exchange);
        System.out.println("[DataBaseManger]创建初始数据已经完成");
    }

//    其他的一些数据库操作
    public void  insertExchange(Exchange exchange){
        metaMapper.insertExchange(exchange);
    }

    public List<Exchange> selectAllExchanges(){
        return metaMapper.selectAllExchanges();
    }
    public void deleteExchange(String exchangeName){
        metaMapper.deleteExchange(exchangeName);
    }

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

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

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

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

    public List<Binding> selectAllBindings(){
        return metaMapper.selectAllBindings();
    }
    public void deleteBinding(Binding binding){
        metaMapper.deleteBinding(binding);
    }
}

但是,上面的metaMapper还没有进行构造,为空,直接运行就会报错,但是这里不能使用@Autowired进行对象注入,因为里不打算把DataBaseManager设置为一个Bean,所以我们就,我们可以在启动类中,对对象metaMapper进行构造。

在启动类中:

java 复制代码
@SpringBootApplication
public class TigerMqApplication {
    public static ConfigurableApplicationContext context;

    public static void main(String[] args) {
        context = SpringApplication.run(TigerMqApplication.class, args);
    }

}

在init()方法中手动获取到metaMapper对象:添加以下代码

java 复制代码
//          手动的获取到MetaMapper
        metaMapper = TigerMqApplication.context.getBean(MetaMapper.class);

五、对数据库操作进行单元测试

创建测试类DataBaseManagerTests

🍅 1、"准备工作"和"收尾工作"

添加两个类,主要是为了放置每个测试用力之间不会收到干扰而创建的。

首先是"准备工作"setUp(),主要是为了调用init()方法,初始化数据库

然后是"收尾工作"tearDown(),主要是为了删除掉.db文件。

@BeforEach指的是每个用例执行前都会调用这个方法

@AfterEach指的是每个用力执行完后调用这个方法

编写代码:

java 复制代码
@SpringBootTest
public class DataBaseMangerTests {
    private DataBaseManger dataBaseManger = new DataBaseManger();

//    编写多个方法,每个方法都是一组单元测试用例
//    编写两个方法,分别用于进行"准备工作"和收尾工作
//    这是为了让每个测试用力之间不会收到干扰而创建的

//使用该方法,进行准备工作,每个用力执行前都要调用这个方法
    @BeforeEach
    public void setUp(){
//        由于init中,需要经过context对象拿到metaMapper示例
//        所以需要先把context对象构造出来
        TigerMqApplication.context = SpringApplication.run(TigerMqApplication.class);
        dataBaseManger.init();
    }



//    该方法用来执行收尾工作,每个用例执行后,需要调用这个方法
    @AfterEach
    public void tearDown(){
//        及那个数据库清空,删掉.db文件
//        删除之前需要关闭context对象。
//        原因是因为context持有了MetaMapper类的实力对象,
//        而该对象打开了meta.db文件,而在打开的情况下,删除操作是不能进行的
        TigerMqApplication.context.close();
        dataBaseManger.deleteDB();
    }
}

🍅 2、编写测试类进行用力测试


🎈测试init()方法

java 复制代码
 @Test
    public void  testInitTable(){
//        由于init()方法已经被调用过了,直接在测试用力代码中检查当前数据库状态
//        从数据库中查询数据是否符合预期
//        查交换机表,会有一个匿名exchange数据
        List<Exchange> exchangeList = dataBaseManger.selectAllExchanges();
        List<MSGQueue> queueList = dataBaseManger.selectAllQueues();
        List<Binding> bindingList = dataBaseManger.selectAllBindings();

//       使用断言
//       判断1和exchangeList是否相等
//       assertEquals(预期值,实际值)
        Assertions.assertEquals(1,exchangeList.size());
        Assertions.assertEquals("",exchangeList.get(0).getName());
        Assertions.assertEquals(ExchangeType.DIRECT,exchangeList.get(0).getType());
        Assertions.assertEquals(0,queueList.size());
        Assertions.assertEquals(0,bindingList.size());
    }

🎈测试 交换机(插入和删除)

* Insert

java 复制代码
private Exchange createTestExchange(String exchangeName){
        Exchange exchange = new Exchange();
        exchange.setName(exchangeName);
        exchange.setType(ExchangeType.FANOUT);
        exchange.setDurable(true);
        return exchange;
    }


@Test
    public void testInsertExchange(){
//        构造一个Exchange对象,插入到数据库中,再查询出来,看是否符合预期
        Exchange exchange = createTestExchange("testExchange");
        dataBaseManger.insertExchange(exchange);
        List<Exchange> exchangeList = dataBaseManger.selectAllExchanges();
        Assertions.assertEquals(2,exchangeList.size());
        Exchange newExchange = exchangeList.get(1);
        Assertions.assertEquals("testExchange",newExchange.getName());
        Assertions.assertEquals(ExchangeType.FANOUT,newExchange.getType());
        Assertions.assertEquals(true,newExchange.isDurable());
    }

* Delete

java 复制代码
@Test
    public void testDeleteExchange(){
//        先构造一个交换机
        Exchange exchange = createTestExchange("testExchange");
        dataBaseManger.insertExchange(exchange);
        List<Exchange> exchangeList = dataBaseManger.selectAllExchanges();
        Assertions.assertEquals(2,exchangeList.size());
        Assertions.assertEquals("testExchange",exchangeList.get(1).getName());

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

🎈 测试DataBaseManager的队列(插入和删除)

* Insert

java 复制代码
private MSGQueue createTestQueue(String queueName){
        MSGQueue queue = new MSGQueue();
        queue.setName(queueName);
        queue.setDurable(true);
        queue.setExclusive(false);
        return queue;
    }
    @Test
    public void testInsertQueue(){
        MSGQueue queue = createTestQueue("testQueue");
        dataBaseManger.insertQueue(queue);

        List<MSGQueue> queueList = dataBaseManger.selectAllQueues();

        Assertions.assertEquals(1,queueList.size());
        MSGQueue newQueue = queueList.get(0);
        Assertions.assertEquals("testQueue",newQueue.getName());
    }

* Delete

java 复制代码
 @Test
    public void testDeleteQueue(){
        MSGQueue queue = createTestQueue("testQueue");
        dataBaseManger.insertQueue(queue);

        List<MSGQueue> queueList = dataBaseManger.selectAllQueues();

        Assertions.assertEquals(1,queueList.size());

//        删除
        dataBaseManger.deleteQueue("testQueue");
        queueList = dataBaseManger.selectAllQueues();
        Assertions.assertEquals(0,queueList.size());
    }

🎈测试Binding

* Insert

java 复制代码
public Binding createTestBinding(String exchangeName,String queueName){
        Binding binding = new Binding();
        binding.setExchangeName(exchangeName);
        binding.setQueueName(queueName);
        binding.setBindingKey("testBindingKey");
        return binding;
    }
    @Test
    public void testInsertBinding(){
        Binding binding = createTestBinding("testExchange","tesQueue");
        dataBaseManger.insertBinding(binding);

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

*Delete

java 复制代码
   @Test
    public void testDeleteBinding(){
        Binding binding = createTestBinding("testExchange","testQueue");
        dataBaseManger.insertBinding(binding);

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

//        删除操作
        Binding toDeleteBinding = createTestBinding("testExchange","testQueue");
        dataBaseManger.deleteBinding(toDeleteBinding);
        bindingList = dataBaseManger.selectAllBindings();
        Assertions.assertEquals(0,bindingList.size());
    }

六、小结

🍅 1、运行时可能会报错

在刚开始测试运行的过程中,代码可能会报错。我在运行的时候就遇到了以下的错误,你们可能也会遇见

解决方案我是上网查到的:

Error creating bean with name 'dataSource' defined in class path resource解决办法_张道长的博客-CSDN博客

🍅 2、已经完成的任务

(1)项目需求分析:项目实战 --- 消息队列(1) {需求分析}_‍️藿香正气水的博客-CSDN博客

(2)设计核心类: 项目实战 --- 消息队列(2){创建核心类}_‍️藿香正气水的博客-CSDN博客

(3)设计数据库,并且针对数据库代码进行了单元测试

相关推荐
看山还是山,看水还是。14 分钟前
MySQL 管理
数据库·笔记·mysql·adb
fishmemory7sec21 分钟前
Koa2项目实战2(路由管理、项目结构优化)
数据库·mongodb·koa
momo小菜pa31 分钟前
【MySQL 09】表的内外连接
数据库·mysql
Jasonakeke39 分钟前
【重学 MySQL】四十九、阿里 MySQL 命名规范及 MySQL8 DDL 的原子化
数据库·mysql
程序猿小D41 分钟前
第二百六十九节 JPA教程 - JPA查询OrderBy两个属性示例
java·开发语言·数据库·windows·jpa
小宇成长录1 小时前
Mysql:数据库和表增删查改基本语句
数据库·mysql·数据库备份
团儿.2 小时前
解锁MySQL高可用新境界:深入探索MHA架构的无限魅力与实战部署
数据库·mysql·架构·mysql之mha架构
程序猿小D2 小时前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa
权^3 小时前
MySQL--聚合查询、联合查询、子查询、合并查询(上万字超详解!!!)
大数据·数据库·学习·mysql
Code成立3 小时前
1、深入理解Redis线程模型
数据库·redis·bootstrap