????????上一篇文章中我把一些關鍵的類以及表示出來,如何對這些類對應的對象進行管理呢?管理分為硬盤和內存上,硬盤又分為數據庫(管理交換機,隊列和綁定)和文件(管理消息),本文就是討論的數據庫上的管理。
? ? ? ? 此處為了使用更加方便,簡化環境,采用更加輕量的數據庫——SQLite,它是一個本地數據庫,相當于直接操作本地的硬盤文件。
? ? ? ? 當在idea中配置好SQLiite數據庫后,就需要建庫建表,由于把配置依賴準備好之后就會自動的建庫,因此我們這里主要關注的是建表,數據庫存儲的是交換機,隊列和綁定,因此應該針對三者建立不同的表。可以根據之前創建的核心類進行設計表。那么上述的建表操作什么時機來執行,可能程序需要反復部署多次,為了簡化部署的步驟,可以通過代碼,自動完成建表的操作。
? ? ? ? 為了自動完成建表操作,首先創建一個接口,內有需要建表的方法,然后實現對應的xml文件,通過xml實現接口中的抽象方法。對與建表操作我們使用undata標簽。最終,我們根據定義的類建立了三張表,但是對于其中的arguments,由于是Map屬性, 為了把arguments 存到數據庫中,需要把Map轉化為json格式的字符串。
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface MetaMapper {// 三個核心建表方法void createExchangeTable();void createQueueTable();void createBindingTable();
}
????????當前,是把每個建表語句,都單獨的列為一個 update 標簽, 并且對應一個 java 方法,能否改成,一個 update 標簽中包含多個 建表語句,同時借助一個 java 方法,完成上述多個表的創建呢? MyBatis 支持,一個 標簽 中包含多個 sq| 語句的(前提是,搭配 MySQL 或者 Oracle).對于 SQLite,無法做到上述功能的,當你一個 update 標簽中,寫了多個 create table 語句的時候,只有第一個語句能執行.
? ? ? ? 如何實現把 arguments 這個鍵值對,和數據庫中的字符串類型相互轉換呢? 關鍵在于, MyBatis 在完成數據庫操作的時候,會自動的調用到對象的 getter 和 setter.
- 比如 MyBatis 往數據庫中寫數據, 就會調用對象的 getter 方法,拿到屬性的值,再往數據庫中寫。如果這個過程中,讓 getArquments 得到的結果是 String 類型的,此時,就可以直接把這個數據寫到數據庫了
- 比如 MyBatis 從數據庫讀數據的時候,就會調用對象的 setter 方法,把數據庫中讀到的結果設置到對象的屬性中.如果這個過程中,讓 setArguments,參數是一個 String,并且在 setArquments 內部針對字符串解析,解析成一個 Map 對象
因此我們需要自己寫Exchange類的getArguments和setArguments方法,其中getArguments用于MyBatis 往數據庫中寫數據,因此將Map轉為Json類型的字符串。從數據庫讀數據之后,構造Exchange對象,會自動調用到setArguments,是把arguments從json格式的字符串轉化為Map
????????第二個參數,用來描述當前 json 字符串, 要轉成的 java 對象是什么類型的.如果是個簡單類型,直接使用對應類型的類對象即可,如果是集合類這樣的復雜類型,可以使用 TypeReference 匿名內部類對象,來描述復雜類型的具體信息,(通過泛型參數來描述的)
????????對于 交換機 和 隊列 這兩個表,由于使用 name 作為主鍵,直接按照 name 進行刪除即可,對于綁定來說,此時沒有主鍵,刪除操作,其實是針對 exchangeName 和 queueName 兩個維度進行篩選.。之后需要在接口中聲明三個核心增刪方法,然后需要在xml文件中寫出insert和delete語句。如下:
?其中的#{}:MyBatis 看到這個, 就會通過 getArguments 方法, 來獲取到這個參數的內容,此處數據庫中期望的類型是 String, 此處也就需要讓 getArguments 能夠得到 String。
????????此時,我們把數據庫的基本操作已經借助MyBatis封裝完成。接下來寫一個類整合上面的操作。首先是數據庫的初始化,此處使用的是一個普通的方法。數據庫的初始化=建庫建表 +插入一些默認數據,我們期望, 在咱們的 broker server 啟動的時候, 做出下列邏輯判定:
1.如果數據庫已經存在了,(表啥的都有了),不做任何操作.
2.如果數據庫不存在, 則創建庫,創建表,構造默認數據
數據庫判斷是否存在就判定 meta.db 這個文件是否存在即可。根據以上邏輯編寫完成代碼之后,發現一些方法涉及到mapper的相關調用,那么此時mapper需要保證是被構造出來的,那么如何進行實例化?Mapper是通過Mybatis進行操作的,換句話說,Mapper已經被注冊到spring里面了,直接從spring里面拿到現成的對象。常用是@Autowired,但是前提是外面的類是一個注冊在spring中的對象,但是現在并不打算讓類是一個Bean對象,因為后面還需要手動進行管理,然后構造整體的結構,因此此時不可以用@Autowired,需要手動的構造。在啟動類添加一個靜態成員,在下面的main方法中,將run方法的返回結果賦值到靜態成員,此時借助這個靜態成員可以手動的獲取指定的bean對象了。接下來在類中完成接口的三個核心insert和delete方法,可以增加select操作。最后進行測試。
? ? ? ? 設計單元測試要求,單元測試用例和用例之間是需要相互獨立的,互不干擾的。因此可以這樣子:每個用例執行之前,先執行一段邏輯,搭建測試的環境,準備好測試用的東西;每個用例執行之后,再執行一段邏輯,把用例執行過程中產生的中間結果的影響給消除掉。即“準備工作”和“收尾工作”,加上注解。
? ? ? ? 準備工作:對數據庫進行初始化操作,由于init方法需要手動獲取metaMapper,依賴于context對象,因此在測試用例中也需要context對象.
? ? ? ? 收尾工作:前面是數據庫初始化,因此這里要清空數據庫,在清空時注意此處不能直接就刪除, 而需要先關閉上述 context 對象!! 此處的 context 對象, 持有了 MetaMapper 的實例, MetaMapper 實例又打開了 meta.db 數據庫文件。如果 meta.db 被別人打開了, 此時的刪除文件操作是不會成功的 (Windows 系統的限制, Linux 則沒這個問題),另一方面, 獲取 context 操作, 會占用 8080 端口. 此處的 close 也是釋放 8080