1. 程式人生 > >SpringBoot整合MongoDB(實現一個簡單快取)

SpringBoot整合MongoDB(實現一個簡單快取)

## 前言 SpringBoot是常用開發框架,而MongoDB也是最近越來越火的非關係型資料庫,這裡使用SpringBoot+MongoDB實現一個小案例,當然MongoDB實際做快取的可能不多,但是這裡僅僅為了一個小demo簡單的學習使用,入門上手為目的,更多的複雜查詢還需關注MongoDB官網。 >如果本篇對你有幫助,還請點贊支援一下!原創作者:`bigsai` 如果對MongoDB不太瞭解,還請先看上篇[MongoDB從立地到成佛](https://bigsai.blog.csdn.net/article/details/108040117)。 ## 建立MongoDB資料庫和專案 ### 建立MongoDB資料庫 開啟Studio 3T資料庫管理工具,連線本地MongoDB資料庫之後,建立名為test的資料庫,在test資料庫中建立名為news得集合: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200822002245635.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) ### 建立專案 首先,開啟IDEA建立專案,選擇建立Springboot專案: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200821210716683.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) 然後在選擇Gruop和Aritifact時候分別填寫**com**和**mongodemo**,Java Version選擇8版本。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200821212212572.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) 在勾選模組時候,這裡勾選Spring web、MongoDB依賴模組,選擇合適位置建立專案,專案就可以成功建立: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200821214801948.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) ### 預備工作 建立完專案,我們需要做一些預備工作用來完成快取。我們首先要在專案中的application.properties中新增配置連線到資料庫,配置規則為:`spring.data.mongodb.uri=mongodb://地址:埠/資料庫名`,本案例使用本地的MongoDB資料庫,預設埠為27017,而使用的MongoDB具體資料庫名稱為test,那麼就可以按照以下進行配置: ```js spring.data.mongodb.uri=mongodb://localhost:27017/test ``` 這樣在專案中就可以連線到本地的MongoDB的test資料庫並訪問。 其次在專案中com.mongodb目錄下分別建立controller,service,pojo資料夾,在controller資料夾下建立`newsController.java`類,為負責url和邏輯的控制器: ```java package com.mongodemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RestController; @RestController public class newsController { private static Logger logger= LoggerFactory.getLogger(newsController.class); } ``` 其中: - @RestController就宣告該類為一個控制器,並且返回JSON字串給前端。 - 而Logger物件用於列印日誌。在web專案中我們更傾向於使用log列印日誌而不在控制檯直接輸出。 controller建立完畢在service 資料夾下建立`NewsService.java`類,裡面先編寫以下內容: ```java package com.mongodemo.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; @Service public class NewsService { private static Logger logger= LoggerFactory.getLogger(NewsService.class); @Autowired MongoTemplate mongoTemplate; } ``` 其中: - @Service 表示該類為一個service(事務處理),可以被注入到其他物件(Spring幫你管理)。 - @Autowired表示要注入物件的意思。而MongoTemplate 就是已經封裝好在Spring中操作MongoDB的物件。 service建立完成,我們需要在pojo中建立news類,代表新聞實體內容。 ```java import java.util.Date; public class news { private String title; private Date date; private String brief; private String content; private String author; @Override public String toString() { return "news{" + "title='" + title + '\'' + ", date=" + date + ", brief='" + brief + '\'' + ", content='" + content + '\'' + ", author='" + author + '\'' + '}'; } public news(String title, Date date, String brief, String content, String author) { this.title = title; this.date = date; this.brief = brief; this.content = content; this.author = author; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getBrief() { return brief; } public void setBrief(String brief) { this.brief = brief; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } } ``` 其中各個欄位分別表示為: |名稱|含義| |--|--| | title| 標題| | date| 日期| |brief|概要| |content|內容| |author|作者| ## 快取查詢 下面開始實戰MongoDB實現一個新聞得快取功能,實現快取之前,要清楚快取的核心作用:提升web程式的**查詢速度**,將熱點資料放到非關係資料庫中。本案例對介面進行快取,不過真正的快取例項需要考慮很多問題比如時效性,快取那些資料等。在這裡主要為了講解MongoDB的一個例項。 在查詢時候,快取和資料庫之間通常是這麼配合的: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200822082605474.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) 為了降低整個專案的複雜度,這裡用手動生成的資料物件代替成資料庫中查詢的資料,我們在NewsService中編寫getNewsByTitle(String title)函式,其功能是根據標題返回快取或資料庫中該條news資料,如果MongoDB中存在則直接返回該物件,否則先從資料庫查詢(這裡直接生成),然後存到MongoDB中再返回。具體程式碼為: ```java public news getNewsByTitle(String title) { //查詢資料先從MongoDB中查詢 Query query = new Query(Criteria.where("title").is(title)); news news=mongoTemplate.findOne(query, news.class); if(news==null)//快取中沒該條記錄 { logger.info("從資料庫查詢資料"); //假設news1從資料庫中查詢 news news1=new news(title,new Date(),"","","bigsai"); news1.setBrief("有了博學谷,媽媽再也不用擔心我的java學習!"); news1.setContent("博學谷優質學習資料為java學習提供更好環境,越來越多開發者學習使用"); mongoTemplate.insert(news1,"news"); logger.info("資料插入到MongoDB成功"); news=news1; } else { logger.info("資料從快取訪問成功"); } return news; } ``` 上面的程式碼中: - 我們核心使用mongoTemplate物件來實現查詢一條記錄,查詢語句為:mongoTemplate.findOne(query, news.class),第一個引數為查詢的條件,第二個引數為查詢結果轉成Java物件的型別,它幫你自動處理。 - 通過Query物件來輔助我們實現條件查詢,這裡的意思就是查詢條件為:MongoDB中title欄位為傳進來title字串的該條記錄。 - 而插入的語法為 mongoTemplate.insert(news1,"news"),第一個引數為插入的文件記錄,第二個引數為連線呃MongoDB對應資料庫下的集合(Collections)。 在newsController中,我們編寫一個名稱為getnews的介面,用來給使用者返回該標題新聞(news類)的一條資料的JSON檔案,具體程式碼為: ```java @Autowired NewsService newsService; @GetMapping("getnews/{title}") public news getnews(@PathVariable String title) { news news=newsService.getNewsByTitle(title); return news; } ``` 上面程式碼中: - @Autowired(required = false)用來注入物件,下面的NewsService userService就是被注入的物件,注入之後不需要手動建立物件可以直接使用(Spring幫你管理) - @GetMapping("getnews/{title}") 意為宣告一個get請求方式的介面, 我們啟動程式,瀏覽器輸入`localhost:8080/getnews/好好學java` 頁面會有返回的結果,返回的一個news物件序列化成JSON的字串的文字。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200822092224899.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) 同時,你檢視IDEA的日誌,由於第一次查詢,MongoDB中沒有對應資料你會發現會先從資料庫中查詢然後儲存到MongoDB中: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200822093748293.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) 檢視MongoDB的news集合發現記錄被成功插入了,多重新整理頁面`localhost:8080/getnews/好好學java`你會發現資料會直接從MongoDB中返回: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200822094157561.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) ## 快取更新、刪除 快取中的資料和儲存的關係資料庫的資料是一致的,當我們只有查詢操作的時候,可以一直保持資料的一致性,但是我們如果對資料有更新、刪除的操作,就需要對關係資料庫和MongoDB中的資料同時進行更新或刪除的操作,讓資料再次達到一致性的效果。 ### 快取更新 雖然大部分情況我們對熱點新聞資料可能很少更新,但是也有時候新聞中有部分內容需要更改的需要我們完成,比如比分錯字或者不妥的言論。 我們在NewsService中編寫updateNewsContentByTitle((String title,String content)函式,其作用為更新資料庫(這裡沒有具體實現)和MongoDB快取中的資料: ```java public boolean updateNewsContentByTitle(String title,String content) { try { Query query = new Query(Criteria.where("title").is(title)); Update update = new Update(); update.set("content", content);//更新內容 update.set("date",new Date());//更新時間 // 假設在這裡資料庫中更新過這裡跳過 // updateFirst 更新查詢返回結果集的第一條 //upsert 更新如果不存在就插入 mongoTemplate.upsert(query, update, news.class); } catch (Exception e) { return false; } return true; } ``` 其中: - Query物件來輔助我們實現條件查詢待更新資料,這裡的意思就是查詢條件同樣為:MongoDB中title欄位為傳進來title字串的該條記錄。 - Update物件用來記錄更新的欄位和資料,這裡更新傳進來的content內容和date日期。 - mongoTemplate.upsert(query, update, news.class)用來實現更新,如果MongoDB中不存在該資料那麼就插入到MongoDB中。 編寫完service,在newsController中編寫一個名為updatenews的介面,用來更新資料庫資料和快取在MongoDB的資料: ```java @GetMapping("updatenews") public String updatenews(String title,String content) { boolean bool=newsService.updateNewsContentByTitle(title,content); if(bool) return "更新成功"; else return "更新失敗"; } ``` 啟動程式訪問`localhost:8080/updatenews?title=好好學java&content=學好java走遍全天下`,你會發現資料更新成功: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200822230840403.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) ### 快取刪除 除了更新的時候需要保證資料一致性,刪除的時候也需要保證資料一致性,如果在刪除關係資料庫的資料而不刪除MongoDB快取,那麼下次查詢該條資料MongoDB中存在而關係資料庫中不存在,這樣就造成了資料不一致,所以在刪除資料的時候我們需要在MongoDB中的資料也刪除。 在NewsService中編寫deleteNewsByTitle(String title)函式,用來根據標題title刪除MongoDB中的記錄: ```java public boolean deleteNewsByTitle(String title) { try { Query query = new Query(Criteria.where("title").is(title)); mongoTemplate.remove(query,news.class); } catch (Exception e) { return false; } return true; } ``` mongoTemplate.remove(query,news.class);意味從MongoDB中刪除滿足查詢條件的記錄。其中query為查詢條件,news.class為刪除物件在Java中的類。 在newsController中編寫deletenews介面,用來處理刪除的請求: ```java @GetMapping("deletenews/{title}") public String deletenews(@PathVariable String title) { try { newsService.deleteNewsByTitle("好好學java"); return "刪除成功"; } catch (Exception e) { return "刪除失敗"; } } ``` 啟動程式,訪問`http://localhost:8080/deletenews/好好學java`,會發現快取在MongoDB中的記錄被成功刪除,這樣就保證MongoDB中不會出現關係資料庫中不存在的髒資料,達到資料一致性! ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200823003706916.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center) 本篇到這裡就結束了,如果幫助還請不要吝嗇你的**小贊、收藏**一份如有更多期待還請關注公眾號`bigsai`,回覆bigsai獲取珍藏pdf資源