根據源碼,模擬實現 RabbitMQ - 通過 SQLite + MyBatis 設計數據庫(2)

目錄

一、數據庫設計

1.1、數據庫選擇

1.2、環境配置

1.3、建庫建表接口實現

1.4、封裝數據庫操作

1.5、針對 DataBaseManager 進行單元測試


一、數據庫設計


1.1、數據庫選擇

MySQL 是我們最熟悉的數據庫,但是這里我們選擇使用 SQLite,原因如下:

  1. SQLite 比 MySQL 更輕量:一個完整的 SQLite 數據庫,只有一個單獨的可執行文件(不到 1M).
  2. SQLite 操作簡便:SQLite 只是一個本地數據庫,相當于是直接操作本地的硬盤.
  3. SQLite 應用也非常廣泛:在一些性能不高的設備上,SQLite 是數據庫的首選,尤其是移動端和嵌入式設備(Android 系統就是內置的 SQLite).

1.2、環境配置

在 java?中直接使用 maven 把 SQLite 依賴引入即可(版本自行考慮)~

        <!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc --><dependency><groupId>org.xerial</groupId><artifactId>sqlite-jdbc</artifactId><version>3.41.2.1</version></dependency>

配置如下

spring:datasource:url: jdbc:sqlite:./data/meta.dbusername:password:driver-class-name: org.sqlite.JDBC

url:SQLite 的工作路徑,用來存儲數據在某個指定的文件中.

username & password:對于 SQLite 來說,不需要使用 用戶名密碼.? MySQL 是一個客戶端服務器結構的程序,而 SQLite 則不是客戶端服務器結構的程序,只有本地主機能訪問.

Ps:SQLite 雖然和 MySQL 不太一樣,但是都可以通過 MyBatis 這樣的框架來使用.

1.3、建庫建表接口實現

存儲的數據就是:交換機、隊列、綁定.

這里我們使用 MyBatis 來完成相關的 CRUD.

mapper 接口中提供三個建庫建表操作和針對這三個庫表進行 CRUD 的操作.

@Mapper
public interface MetaMapper {//三個核心建表方法void createExchangeTable();void createQueueTable();void createBindingTable();//基于上述三個表,進行 插入、刪除、查詢 操作void insertExchange(Exchange exchange);List<Exchange> selectAllExchange();void deleteExchange(String exchangeName);void insertQueue(MSGQueue queue);List<MSGQueue> selectAllQueue();void deleteQueue(String queueName);void insertBinding(Binding binding);List<Binding> selectAllBinding();void deleteBinding(Binding binding);}

對應的實現如下:

    <update id="createExchangeTable">create table if not exists exchange (name varchar(50) primary key,type int,durable boolean,autoDelete boolean,arguments varchar(1024));</update><update id="createQueueTable">create table if not exists queue (name varchar(50) primary key,durable boolean,exclusive boolean,autoDelete boolean,arguments varchar(1024));</update><update id="createBindingTable">create table if not exists binding (exchangeName varchar(50),queueName varchar(50),bindingKey varchar(256))</update><insert id="insertExchange" parameterType="com.example.rabbitmqproject.mqserver.core.Exchange">insert into exchange values (#{name}, #{type}, #{durable}, #{autoDelete}, #{arguments});</insert><select id="selectAllExchange" resultType="com.example.rabbitmqproject.mqserver.core.Exchange">select * from exchange;</select><delete id="deleteExchange" parameterType="com.example.rabbitmqproject.mqserver.core.Exchange">delete from exchange where name = #{name};</delete><insert id="insertQueue" parameterType="com.example.rabbitmqproject.mqserver.core.MSGQueue">insert into queue values(#{name}, #{durable}, #{exclusive}, #{autoDelete}, #{arguments});</insert><select id="selectAllQueue" resultType="com.example.rabbitmqproject.mqserver.core.MSGQueue">select * from queue;</select><delete id="deleteQueue">delete from queue where name = #{name};</delete><insert id="insertBinding">insert into binding values (#{exchangeName}, #{queueName}, #{bindingKey});</insert><select id="selectAllBinding" resultType="com.example.rabbitmqproject.mqserver.core.Binding">select * from binding;</select><delete id="deleteBinding">delete from binding where exchangeName = #{exchangeName} and queueName = #{queueName};</delete>

1.4、封裝數據庫操作

這里我們通過定制化 代碼 的方式來自動完成建庫建表的操作(符合?RabbitMQ 中間件的設定).

創建 DataBaseManager 類,來完成數據庫相關的操作,注意細節如下:

  1. 初始化方法:一般談到初始化,都會用到 構造方法,但是這里我們使用一個 普通的方法 —— init();構造方法一般是用來初始化類的屬性,不會涉及到太多的業務邏輯,而此處的初始化,帶有業務邏輯,還是單獨領出來,手動來調用比較合適.
  2. 建庫建表邏輯:這里期望,broker server 啟動的時候做出如下邏輯判斷:
    1. 如果數據庫已經存在(表存在),不做任何操作.
    2. 如果數據庫不存在,則建庫建表,構造默認數據.

Ps:怎么判定數據庫存在或者不存在?就判定 meta.db 文件是否存在即可(配置文件中的 url).

public class DataBaseManager {//這里不使用 Autowired 注解獲取,因為當前這個類需要我們后面手動管理private MetaMapper metaMapper;//針對數據庫進行初始化public void init() {//手動獲取到 MetaMappermetaMapper = RabbitmqProjectApplication.context.getBean(MetaMapper.class);if(!checkDBExists()) {//數據庫不存在,就進行建庫建表操作//先創建出目錄結構(否則會報錯:找不到目錄結構)File dataDir = new File("./data");dataDir.mkdirs();//創建數據庫createTable();//插入默認數據createDefaultData();System.out.println("[DataBaseManager] 數據庫初始化完成!");} else {//數據庫存在,什么都不做即可System.out.println("[DataBaseManager] 數據庫已存在!");}}private boolean checkDBExists() {File file = new File("./data/meta.db");return file.exists();}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] 創建初始數據完成!");}//把數據庫中其他操作也在這里封裝一下public void insertExchange(Exchange exchange) {metaMapper.insertExchange(exchange);}public List<Exchange> selectAllExchange() {return metaMapper.selectAllExchange();}public void deleteExchange(String exchangeName) {metaMapper.deleteExchange(exchangeName);}public void insertQueue(MSGQueue queue) {metaMapper.insertQueue(queue);}public List<MSGQueue> selectAllQueue() {return metaMapper.selectAllQueue();}public void deleteQueue(String queueName) {metaMapper.deleteQueue(queueName);}public void insertBinding(Binding binding) {metaMapper.insertBinding(binding);}public List<Binding> selectAllBinding() {return metaMapper.selectAllBinding();}public void deleteBinding(Binding binding) {metaMapper.deleteBinding(binding);}public void deleteDB() {//刪除文件File file = new File("./data/meta.db");boolean res = file.delete();if(res) {System.out.println("[DataBaseManager] 數據庫文件刪除完畢!");} else {System.out.println("[DataBaseManager] 數據庫文件刪除失敗!");}//刪除目錄File dataDir = new File("./data");boolean ret = dataDir.delete();if(ret) {System.out.println("[DataBaseManager] 數據庫目錄刪除完成!");} else {System.out.println("[DataBaseManager] 數據庫目錄刪除失敗!");}}}

1.5、針對 DataBaseManager 進行單元測試

設計單元測試,這里的要求就是單元測試用例和用例之間是需要相互獨立的,不會干擾,例如以下情況:

測試過程中,向數據庫中插入數據 a .

在針對 b 進行測試,可能 a 這里的數據會對 b 造成干擾.

Ps:這里不一定是數據庫,也可能是其他方面,例如是否搞了一個文件,是否占用了端口...

@SpringBootTest
public class DataBaseManagerTests {private DataBaseManager dataBaseManager = new DataBaseManager();@BeforeEachpublic void setUp() {RabbitmqProjectApplication.context = SpringApplication.run(RabbitmqProjectApplication.class);dataBaseManager.init();}@AfterEachpublic void setclose() {//此處不能直接刪除 數據庫文件 ,需要先關閉 context 對象//此處 context 對象持有了 MetaMapper 的實例, MetaMapper 又打開了 meta.db 數據庫//如果 meta.db 被別人打開了,此時刪除文件是不會成功的(Windows 系統限制, Linux 則不會)//另一方面 context 會占用 8080 端口,此處的 close 也是釋放 8080 端口RabbitmqProjectApplication.context.close();dataBaseManager.deleteDB();}@Testpublic void testInitTable() {List<Exchange> exchanges = dataBaseManager.selectAllExchange();List<MSGQueue> msgQueues = dataBaseManager.selectAllQueue();List<Binding> bindings = dataBaseManager.selectAllBinding();Assertions.assertEquals(1, exchanges.size());Assertions.assertEquals("", exchanges.get(0).getName());Assertions.assertEquals(ExchangeType.DIRECT, exchanges.get(0).getType());Assertions.assertEquals(0, msgQueues.size());Assertions.assertEquals(0, bindings.size());}private Exchange createTestExchange(String exchangeName) {Exchange  exchange = new Exchange();exchange.setName(exchangeName);exchange.setType(ExchangeType.FANOUT);exchange.setDurable(true);exchange.setAutoDelete(false);exchange.setArguments("aaa", 1);exchange.setArguments("bbb", 2);return exchange;}@Testpublic void insertExhangeTest() {Exchange exchange = createTestExchange("testExchange");dataBaseManager.insertExchange(exchange);List<Exchange> exchanges = dataBaseManager.selectAllExchange();Assertions.assertEquals(2, exchanges.size());Exchange testExchange = exchanges.get(1);Assertions.assertEquals("testExchange", testExchange.getName());Assertions.assertEquals(ExchangeType.FANOUT, testExchange.getType());Assertions.assertEquals(true, testExchange.isDurable());Assertions.assertEquals(false, testExchange.isAutoDelete());Assertions.assertEquals(1, testExchange.getArguments("aaa"));Assertions.assertEquals(2, testExchange.getArguments("bbb"));}@Testpublic void deleteExchangeTest() {Exchange exchange = createTestExchange("testExchange");dataBaseManager.insertExchange(exchange);List<Exchange> exchanges = dataBaseManager.selectAllExchange();Assertions.assertEquals(2, exchanges.size());Assertions.assertEquals("testExchange", exchanges.get(1).getName());//刪除dataBaseManager.deleteExchange("testExchange");exchanges = dataBaseManager.selectAllExchange();Assertions.assertEquals(1, exchanges.size());}private MSGQueue createTestQueue(String queueName) {MSGQueue queue = new MSGQueue();queue.setName(queueName);queue.setDurable(true);queue.setExclusive(false);queue.setAutoDelete(false);queue.setArguments("aaa", 1);queue.setArguments("bbb", 2);return queue;}@Testpublic void testInsertQueue() {MSGQueue queue = createTestQueue("testQueue");dataBaseManager.insertQueue(queue);List<MSGQueue> queues = dataBaseManager.selectAllQueue();Assertions.assertEquals(1, queues.size());MSGQueue msgQueue = queues.get(0);Assertions.assertEquals("testQueue", msgQueue.getName());Assertions.assertEquals(true, msgQueue.isDurable());Assertions.assertEquals(false, msgQueue.isExclusive());Assertions.assertEquals(false, msgQueue.isAutoDelete());Assertions.assertEquals(1, msgQueue.getArguments("aaa"));Assertions.assertEquals(2, msgQueue.getArguments("bbb"));}@Testpublic void testDeleteQueue() {MSGQueue queue = createTestQueue("testQueue");dataBaseManager.insertQueue(queue);List<MSGQueue> queues = dataBaseManager.selectAllQueue();Assertions.assertEquals(1, queues.size());//刪除dataBaseManager.deleteQueue("testQueue");queues = dataBaseManager.selectAllQueue();Assertions.assertEquals(0, queues.size());}private Binding createTestBinding(String exchangeName, String queueName) {Binding binding = new Binding();binding.setExchangeName(exchangeName);binding.setQueueName(queueName);binding.setBindingKey("testBindingKey");return binding;}@Testpublic void testInsertAndDeleteBinding() {Binding binding = createTestBinding("testExchange", "testQueue");dataBaseManager.insertBinding(binding);List<Binding> bindingList = dataBaseManager.selectAllBinding();Assertions.assertEquals(1, bindingList.size());binding = bindingList.get(0);Assertions.assertEquals("testExchange", binding.getExchangeName());Assertions.assertEquals("testQueue", binding.getQueueName());Assertions.assertEquals("testBindingKey", binding.getBindingKey());//刪除dataBaseManager.deleteBinding(binding);bindingList = dataBaseManager.selectAllBinding();Assertions.assertEquals(0, bindingList.size());}}

當然,我只是做了簡單的設計測試用例,實際上站在更嚴謹的角度,還需要設計更豐富的測試用例~

相比于 功能/業務代碼,測試用例代碼編寫起來雖然比較無聊,但是重要性是非常大的,這些操作會大大提高整個項目的開發效率.

Ps:單元測試,本來就是開發要搞的活,寫代碼不可能沒有 bug,進行周密的測試,是應對 bug 最有效的手段.

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39918.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39918.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39918.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

手機出現 不讀卡 / 無信號時應該怎么辦?

當手機屏幕亮起&#xff0c;一般在屏幕最上方都會有代表手機卡狀態的顯示&#xff0c;其中網絡信號和讀卡狀態的標識&#xff0c;依舊有很多人分不太清&#xff0c;更不清楚改怎么辦了。 1、當我們的手機里有兩張卡時&#xff0c;則會有兩個信號顯示 2、信號狀態一般是由短到…

CSS自己實現一個步驟條

前言 步驟條是一種用于引導用戶按照特定流程完成任務的導航條&#xff0c;在各種分步表單交互場景中廣泛應用。例如&#xff1a;在HIS系統-門診醫生站中的接診場景中&#xff0c;我們就可以使用步驟條來實現。她的執行步驟分別是&#xff1a;門診病歷>遺囑錄入>完成接診…

ArcGIS Pro基礎入門、制圖、空間分析、影像分析、三維建模、空間統計分析與建模、python融合、案例全流程科研能力提升

目錄 第一章 入門篇 GIS理論及ArcGIS Pro基礎 第二章 基礎篇 ArcGIS數據管理與轉換 第三章 數據編輯與查詢、拓撲檢查 第四章 制圖篇 地圖符號與版面設計 第五章 空間分析篇 ArcGIS矢量空間分析及應用 第六章 ArcGIS柵格空間分析及應用 第七章 影像篇 遙感影像處理 第八…

Python random模塊用法整理

隨機數在計算機科學領域扮演著重要的角色&#xff0c;用于模擬真實世界的隨機性、數據生成、密碼學等多個領域。Python 中的 random 模塊提供了豐富的隨機數生成功能&#xff0c;本文整理了 random 模塊的使用。 文章目錄 Python random 模塊注意事項Python random 模塊的內置…

大語言模型控制生成的過程Trick:自定義LogitsProcessor實踐

前言 在大模型的生成過程中&#xff0c;部分原生的大語言模型未經過特殊的對齊訓練&#xff0c;往往會“胡說八道”的生成一些敏感詞語等用戶不想生成的詞語&#xff0c;最簡單粗暴的方式就是在大模型生成的文本之后&#xff0c;添加敏感詞庫等規則手段進行敏感詞過濾&#xf…

30行JS代碼帶你手寫自動回復語音聊天機器人

&#x1f942;(???)您的點贊&#x1f44d;?評論&#x1f4dd;?收藏?是作者創作的最大動力&#x1f91e; 前言 現如今生活中到處都是聊天機器人的身影&#xff0c;聊天機器人不僅僅能減少人工的聊天壓力&#xff0c;而且十分的可愛有趣&#xff0c;安卓系統的小AI&#xf…

Springboot整合Mybatis調用Oracle存儲過程

1、配置說明 Oracel11g+springboot2.7.14+mybatis3.5.13 目標:springboot整合mybatis訪問oracle中的存儲過程,存儲過程返回游標信息。 mybatis調用oracle中的存儲過程方式 2、工程結構 3、具體實現 3.1、在Oracle中創建測試數據庫表 具體數據可自行添加 create table s…

Lodash——使用與實例

1. 簡介 Lodash是一個一致性、模塊化、高性能的JavaScript實用庫。Lodash通過降低array、number、objects、string等等的使用難度從而讓JavaScript變得簡單。Lodash的模塊方法&#xff0c;非常適用于&#xff1a; 遍歷array、object 和 string對值進行操作和檢測創建符合功能的…

字符個數統計(同類型只統計一次)

思路&#xff1a;因為題目圈定出現的字符都是 ascii 值小于等于127的字符&#xff0c;因此只需要定義一個標記數組大小為128 &#xff0c;然后將字符作為數組下標在數組中進行標記&#xff0c;若數組中沒有標記過表示第一次出現&#xff0c;進行計數&#xff0c;否則表示重復字…

簡單線性回歸:預測事物間簡單關系的利器

文章目錄 &#x1f340;簡介&#x1f340;什么是簡單線性回歸&#xff1f;&#x1f340;簡單線性回歸的應用場景使用步驟&#xff1a;注意事項&#xff1a; &#x1f340;代碼演示&#x1f340;結論 &#x1f340;簡介 在數據科學領域&#xff0c;線性回歸是一種基本而強大的統…

Kali Linux助您網絡安全攻防實戰

Kali Linux&#xff1a;黑客與防御者的神器 Kali Linux是一款專為網絡安全測試和攻防實踐而設計的操作系統。它匯集了大量的安全工具&#xff0c;可以用于滲透測試、漏洞掃描、密碼破解等任務&#xff0c;不僅為黑客提供了強大的攻擊能力&#xff0c;也為安全防御者提供了測試和…

Kafka 入門到起飛 - 什么是 HW 和 LEO?何時更新HW和LEO呢?

上文我們已經學到&#xff0c; 一個Topic&#xff08;主題&#xff09;會有多個Partition&#xff08;分區&#xff09;為了保證高可用&#xff0c;每個分區有多個Replication&#xff08;副本&#xff09;副本分為Leader 和 Follower 兩個角色&#xff0c;Follower 從Leader同…

爬蟲逆向實戰(十八)--某得科技登錄

一、數據接口分析 主頁地址&#xff1a;某得科技 1、抓包 通過抓包可以發現數據接口是AjaxLogin 2、判斷是否有加密參數 請求參數是否加密&#xff1f; 查看“載荷”模塊可以發現有一個password加密參數和一個__RequestVerificationToken 請求頭是否加密&#xff1f; 無…

【Linux】Reactor模式

Reactor模式 Reactor模式的定義 Reactor反應器模式&#xff0c;也叫做分發者模式或通知者模式&#xff0c;是一種將就緒事件派發給對應服務處理程序的事件設計模式。 Reactor模式的角色構成 Reactor主要由以下五個角色構成&#xff1a; reactor模式的角色 角色解釋Handle(句…

保姆級別講解Python數據處理,你絕對能會

名字&#xff1a;阿玥的小東東 學習&#xff1a;Python、C/C 主頁鏈接&#xff1a;阿玥的小東東的博客_CSDN博客-python&&c高級知識,過年必備,C/C知識講解領域博主 目錄 1. 文件讀取 2. 數據處理 3. 處理結果輸出 總的來說 為了咱們讓程序跑起來&#xff0c;我們需…

DAY3,ARM(LED點燈實驗)

1.匯編實現開發板三盞燈點亮熄滅&#xff1b; .text .global _start _start: /**********LED123點燈**************/RCC_INIT:1使能PE10 PF10 PE8RCC..寄存器,E[4]1 F[5]1 0x50000a28ldr r0,0x50000a28ldr r1,[r0]orr r1,r1,#(0x3 << 4)str r1,[r0]LED1_INET:2初始化LED…

酷開系統 | 酷開科技大數據,更好的與目標消費人群建立聯系

眾所周知&#xff0c;OTT的一大優勢在于強曝光&#xff0c;能夠給消費者帶來強烈的視覺沖擊&#xff0c;強化品牌認知。但是&#xff0c;要想達到提升品牌認知&#xff0c;首先要保證OTT的流量規模&#xff0c;實現對目標人群的有效覆蓋。得年輕消費者得“天下”&#xff0c;年…

tk切換到mac的code分享

文章目錄 前言一、基礎環境配置二、開發軟件與擴展1.用到的開發軟件與平替、擴展情況 總結 前言 最近換上了coding人生的第一臺mac&#xff0c;以前一直偏好tk&#xff0c;近來身邊的朋友越來越多的用mac了&#xff0c;win的自動更新越來越占磁盤了&#xff0c;而且win11拋棄了…

vue elementui v-for 循環el-table-column 第一列數據變到最后一個

這個動態渲染table表格時發現el-table-column 第一列數據變到最后一個 序號被排到后面 代碼 修改后 <el-table:data"tableData"tooltip-effect"dark"style"width: 100%"height"500"><template v-for"(item, index) i…

PostCSS在vue中的使用

1、安裝 PostCSS 和所需的插件。在命令行中運行以下命令&#xff1a; npm install postcss autoprefixer cssnano postcss-pxtorem --save-dev 這將安裝 PostCSS、Autoprefixer、CSSnano 和 postcss-pxtorem 插件&#xff0c;同時將它們添加到項目的開發依賴中。 2、在項目根目…