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

  • 2020 年 9 月 24 日
  • 筆記

前言

SpringBoot是常用開發框架,而MongoDB也是最近越來越火的非關係型資料庫,這裡使用SpringBoot+MongoDB實現一個小案例,當然MongoDB實際做快取的可能不多,但是這裡僅僅為了一個小demo簡單的學習使用,入門上手為目的,更多的複雜查詢還需關注MongoDB官網。

如果本篇對你有幫助,還請點贊支援一下!原創作者:bigsai

如果對MongoDB不太了解,還請先看上篇MongoDB從立地到成佛

創建MongoDB資料庫和項目

創建MongoDB資料庫

打開Studio 3T資料庫管理工具,連接本地MongoDB資料庫之後,創建名為test的資料庫,在test資料庫中創建名為news得集合:

在這裡插入圖片描述

創建項目

首先,打開IDEA創建項目,選擇創建Springboot項目:
在這裡插入圖片描述
然後在選擇Gruop和Aritifact時候分別填寫commongodemo,Java Version選擇8版本。
在這裡插入圖片描述

在勾選模組時候,這裡勾選Spring web、MongoDB依賴模組,選擇合適位置創建項目,項目就可以成功創建:

在這裡插入圖片描述

預備工作

創建完項目,我們需要做一些預備工作用來完成快取。我們首先要在項目中的application.properties中添加配置連接到資料庫,配置規則為:spring.data.mongodb.uri=mongodb://地址:埠/資料庫名,本案例使用本地的MongoDB資料庫,默認埠為27017,而使用的MongoDB具體資料庫名稱為test,那麼就可以按照以下進行配置:

spring.data.mongodb.uri=mongodb://localhost:27017/test

這樣在項目中就可以連接到本地的MongoDB的test資料庫並訪問。

其次在項目中com.mongodb目錄下分別創建controller,service,pojo文件夾,在controller文件夾下創建newsController.java類,為負責url和邏輯的控制器:

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類,裡面先編寫以下內容:

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類,代表新聞實體內容。

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的一個實例。

在查詢時候,快取和資料庫之間通常是這麼配合的:

在這裡插入圖片描述

為了降低整個項目的複雜度,這裡用手動生成的數據對象代替成資料庫中查詢的數據,我們在NewsService中編寫getNewsByTitle(String title)函數,其功能是根據標題返回快取或資料庫中該條news數據,如果MongoDB中存在則直接返回該對象,否則先從資料庫查詢(這裡直接生成),然後存到MongoDB中再返回。具體程式碼為:

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文件,具體程式碼為:

  @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的字元串的文本。
在這裡插入圖片描述

同時,你查看IDEA的日誌,由於第一次查詢,MongoDB中沒有對應數據你會發現會先從資料庫中查詢然後存儲到MongoDB中:
在這裡插入圖片描述
查看MongoDB的news集合發現記錄被成功插入了,多刷新頁面localhost:8080/getnews/好好學java你會發現數據會直接從MongoDB中返回:
在這裡插入圖片描述

快取更新、刪除

快取中的數據和存儲的關係資料庫的數據是一致的,當我們只有查詢操作的時候,可以一直保持數據的一致性,但是我們如果對數據有更新、刪除的操作,就需要對關係資料庫和MongoDB中的數據同時進行更新或刪除的操作,讓數據再次達到一致性的效果。

快取更新

雖然大部分情況我們對熱點新聞數據可能很少更新,但是也有時候新聞中有部分內容需要更改的需要我們完成,比如比分錯字或者不妥的言論。

我們在NewsService中編寫updateNewsContentByTitle((String title,String content)函數,其作用為更新資料庫(這裡沒有具體實現)和MongoDB快取中的數據:

 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的數據:

 @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走遍全天下,你會發現數據更新成功:
在這裡插入圖片描述

快取刪除

除了更新的時候需要保證數據一致性,刪除的時候也需要保證數據一致性,如果在刪除關係資料庫的數據而不刪除MongoDB快取,那麼下次查詢該條數據MongoDB中存在而關係資料庫中不存在,這樣就造成了數據不一致,所以在刪除數據的時候我們需要在MongoDB中的數據也刪除。

在NewsService中編寫deleteNewsByTitle(String title)函數,用來根據標題title刪除MongoDB中的記錄:

 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介面,用來處理刪除的請求:

 @GetMapping("deletenews/{title}")
 public String deletenews(@PathVariable String title)
 {
     try {
         newsService.deleteNewsByTitle("好好學java");
         return "刪除成功";
     }
     catch (Exception e)
     {
         return "刪除失敗";
     }
 }

啟動程式,訪問//localhost:8080/deletenews/好好學java,會發現快取在MongoDB中的記錄被成功刪除,這樣就保證MongoDB中不會出現關係資料庫中不存在的臟數據,達到數據一致性!
在這裡插入圖片描述

本篇到這裡就結束了,如果幫助還請不要吝嗇你的小贊、收藏一份如有更多期待還請關注公眾號bigsai,回復bigsai獲取珍藏pdf資源一份!