tensquare-3 文件型資料庫MongoDB

一隻想飛的豬*發表於2020-10-25

第3章-文件型資料庫MongoDB

1 MongoDB簡介

1.1 吐槽和評論資料特點分析

吐槽和評論兩項功能存在以下特點:
(1)資料量大
(2)寫入操作頻繁
(3)價值較低 對於這樣的資料,我們更適合使用MongoDB來實現資料的儲存

1.2 什麼是MongoDB

MongoDB 是一個跨平臺的,面向文件的資料庫,是當前 NoSQL 資料庫產品中最熱 門的一種。它介於關聯式資料庫和非關聯式資料庫之間,是非關聯式資料庫當中功能最豐富,最 像關聯式資料庫的產品。它支援的資料結構非常鬆散,是類似 JSON 的 BSON 格式,因此可以 儲存比較複雜的資料型別。 MongoDB 的官方網站地址是:http://www.mongodb.org/

1.3 MongoDB特點

MongoDB 最大的特點是他支援的查詢語言非常強大,其語法有點類似於物件導向 的查詢語言,幾乎可以實現類似關聯式資料庫單表查詢的絕大部分功能,而且還支援對資料建 立索引。它是一個面向集合的,模式自由的文件型資料庫。 具體特點總結如下:
(1)面向集合儲存,易於儲存物件型別的資料
(2)模式自由
(3)支援動態查詢
(4)支援完全索引,包含內部物件
(5)支援複製和故障恢復
(6)使用高效的二進位制資料儲存,包括大型物件(如視訊等)
(7)自動處理碎片,以支援雲端計算層次的擴充套件性
(8)支援 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++語言的驅動程 序,社群中也提供了對 Erlang 及.NET 等平臺的驅動程式
(9) 檔案儲存格式為 BSON(一種 JSON 的擴充套件)

1.4 MongoDB體系結構

MongoDB 的邏輯結構是一種層次結構。主要由: 文件(document)、集合(collection)、資料庫(database)這三部分組成的。邏輯結構是面 向使用者的,使用者使用 MongoDB 開發應用程式使用的就是邏輯結構。
(1)MongoDB 的文件(document),相當於關聯式資料庫中的一行記錄。
(2)多個文件組成一個集合(collection),相當於關聯式資料庫的表。
(3)多個集合(collection),邏輯上組織在一起,就是資料庫(database)。
(4)一個 MongoDB 例項支援多個資料(database)。
下表是MongoDB與MySQL資料庫邏輯結構概念的對比
在這裡插入圖片描述

1.5 資料型別

基本資料型別
null:用於表示空值或者不存在的欄位,{“x”:null}
布林型:布林型別有兩個值true和false,{“x”:true}
數值:shell預設使用64為浮點型數值。{“x”:3.14}或{“x”:3}。對於整型值,可以使用 NumberInt(4位元組符號整數)或NumberLong(8位元組符號整數), {“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}
字串:UTF-8字串都可以表示為字串型別的資料,{“x”:“呵呵”}
日期:日期被儲存為自新紀元依賴經過的毫秒數,不儲存時區,{“x”:new Date()}
正規表示式:查詢時,使用正規表示式作為限定條件,語法與JavaScript的正規表示式相 同,{“x”?[abc]/}
陣列:資料列表或資料集可以表示為陣列,{“x”: [“a“,“b”,”c”]}
內嵌文件:文件可以巢狀其他文件,被巢狀的文件作為值來處理,{“x”:{“y”:3 }}
物件Id:物件id是一個12位元組的字串,是文件的唯一標識,{“x”: objectId() }
二進位制資料:二進位制資料是一個任意位元組的字串。它不能直接在shell中使用。如果要 將非utf-字元儲存到資料庫中,二進位制資料是唯一的方式。
程式碼:查詢和文件中可以包括任何JavaScript程式碼,{“x”:function(){/…/}}

2 走進MongoDB

1.5.2 Docker 環境下MongoDB安裝
在宿主機建立mongo容器

 docker run -di --name=tensquare_mongo ‐p 27017:27017 

MongoDB的圖形化管理工具可以使用NoSQLBooster for MongoDB
以吐槽表為例講解MongoDB常用命令
在這裡插入圖片描述

2.2 常用命令

2.2.1 選擇和建立資料庫

選擇和建立資料庫的語法格式:
use 資料庫名稱
如果資料庫不存在則自動建立
以下語句建立spit資料庫

 use spitdb

2.2.2 插入與查詢文件

插入文件的語法格式:db.集合名稱.insert(資料);
我們這裡可以插入以下測試資料:

db.spit.insert({content:"聽說你很帥呀",userid:"1011",nickname:"小 雅",visits:NumberInt(902)})

查詢集合的語法格式:
db.集合名稱.find()
如果我們要查詢spit集合的所有文件,我們輸入以下命令
db.spit.find()
這裡你會發現每條文件會有一個叫_id的欄位,這個相當於我們原來關聯式資料庫中表的主 鍵,當你在插入文件記錄時沒有指定該欄位,MongoDB會自動建立,其型別是ObjectID 型別。如果我們在插入文件記錄時指定該欄位也可以,其型別可以是ObjectID型別,也 可以是MongoDB支援的任意型別。
輸入以下測試語句:

db.spit.insert({_id:"1",content:"我還是沒有想明白到底為啥出 錯",userid:"1012",nickname:"小明",visits:NumberInt(2020)}); 
db.spit.insert({_id:"2",content:"加班到半夜",userid:"1013",nickname:"凱 撒",visits:NumberInt(1023)}); 
db.spit.insert({_id:"3",content:"手機流量超了咋 辦?",userid:"1013",nickname:"凱撒",visits:NumberInt(111)}); 
db.spit.insert({_id:"4",content:"堅持就是勝利",userid:"1014",nickname:"諾 諾",visits:NumberInt(1223)});

如果我想按一定條件來查詢,比如我想查詢userid為1013的記錄,怎麼辦?很簡單!只 要在find()中新增引數即可,引數也是json格式,如下:

db.spit.find({userid:'1013'})

如果你只需要返回符合條件的第一條資料,我們可以使用findOne命令來實現

db.spit.findOne({userid:'1013'})

如果你想返回指定條數的記錄,可以在find方法後呼叫limit來返回結果,例如:

db.spit.find().limit(3) 

2.2.3 修改與刪除文件

修改文件的語法結構:

db.集合名稱.update(條件,修改後的資料)
如果我們想修改_id為1的記錄,瀏覽量為1000,輸入以下語句:

db.spit.update({_id:"1"},{visits:NumberInt(1000)})

執行後,我們會發現,這條文件除了visits欄位其它欄位都不見了,為了解決這個問題, 我們需要使用修改器$set來實現,命令如下:

db.spit.update({_id:"2"},{$set:{visits:NumberInt(2000)}})

刪除文件的語法結構:
db.集合名稱.remove(條件)
以下語句可以將資料全部刪除,請慎用

db.spit.remove({})

如果刪除visits=1000的記錄,輸入以下語句

db.spit.remove({visits:1000})

2.2.4 統計條數

統計記錄條件使用count()方法。以下語句統計spit集合的記錄數

db.spit.count()

如果按條件統計 ,例如:統計userid為1013的記錄條數

db.spit.count({userid:"1013"})

2.2.5 模糊查詢

MongoDB的模糊查詢是通過正規表示式的方式實現的。格式為:
/模糊查詢字串/
例如,我要查詢吐槽內容包含“流量”的所有文件,程式碼如下:

db.spit.find({content:/流量/})

如果要查詢吐槽內容中以“加班”開頭的,程式碼如下:

db.spit.find({content:/^加班/})

2.2.6 大於 小於 不等於

<, <=, >, >= 這個操作符也是很常用的,格式如下:

db.集合名稱.find({ "field" : { $gt: value }}) // 大於: field > value 
db.集合名稱.find({ "field" : { $lt: value }}) // 小於: field < value 
db.集合名稱.find({ "field" : { $gte: value }}) // 大於等於: field >= value 
db.集合名稱.find({ "field" : { $lte: value }}) // 小於等於: field <= value 
db.集合名稱.find({ "field" : { $ne: value }}) // 不等於: field != value

示例:查詢吐槽瀏覽量大於1000的記錄

db.spit.find({visits:{$gt:1000}})

2.2.7 包含與不包含

包含使用$in操作符。
示例:查詢吐槽集合中userid欄位包含1013和1014的文件

db.spit.find({userid:{$in:["1013","1014"]}})

不包含使用$nin操作符。
示例:查詢吐槽集合中userid欄位不包含1013和1014的文件

db.spit.find({userid:{$nin:["1013","1014"]}})

2.2.8 條件連線

我們如果需要查詢同時滿足兩個以上條件,需要使用$and操作符將條件進行關聯。(相 當於SQL的and)
格式為:

 $and:[ { },{ },{ } ]

示例:查詢吐槽集合中visits大於等於1000 並且小於2000的文件

db.spit.find({$and:[ {visits:{$gte:1000}} ,{visits:{$lt:2000} }]})

如果兩個以上條件之間是或者的關係,我們使用 操作符進行關聯,與前面and的使用 方式相同
格式為:

$or:[ { },{ },{ } ]

示例:查詢吐槽集合中userid為1013,或者瀏覽量小於2000的文件記錄

db.spit.find({$or:[ {userid:"1013"} ,{visits:{$lt:2000} }]})

2.2.9 列值增長

如果我們想實現對某列值在原有值的基礎上進行增加或減少,可以使用$inc運算子來實現

db.spit.update({_id:"2"},{$inc:{visits:NumberInt(1)}} )

3 Java操作MongoDB

3.1 SpringDataMongoDB

SpringData家族成員之一,用於操作MongoDb的持久層框架,封裝了底層的mongodb- driver。
我們十次方專案的吐槽微服務就採用SpringDataMongoDB框架

4 吐槽微服務

4.1 需求分析

採用SpringDataMongoDB框架實現吐槽微服務的持久層。 實現功能:
(1)基本增刪改查API
(2)根據上級ID查詢吐槽列表
(3)吐槽點贊
(4)釋出吐槽

4.2 程式碼編寫

4.2.1 模組搭建

(1)搭建子模組 tensquare_spit
(2)pom.xml引入依賴

<dependency>
            <groupId>com.tensquare</groupId>
            <artifactId>tensquare_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

(3)建立application.yml

server:
  port: 9006
spring:
  application:
    name: tensquare-spit
  data:
    mongodb:
      host: 192.168.11.131
      database: spitdb

(4)建立啟動類

@SpringBootApplication
public class SpitApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpitApplication.class);
    }

    @Bean
    public IdWorker idWorker(){
        return new IdWorker(1,1);
    }
}

4.2.2基本增刪改查API實現

(1)建立實體類
建立包com.tensquare.spit,包下建包pojo 用於存放實體類,建立實體類

package com.tensquare.search.spit.pojo;

import org.springframework.data.annotation.Id;

import java.io.Serializable;
import java.util.Date;

/**
 * 吐槽
 */
public class Spit implements Serializable {
    @Id
    private String _id;
    private String content;
    private Date publishtime;
    private String userid;
    private String nickname;
    private Integer visits;
    private Integer thumbup;
    private Integer share;
    private Integer comment;
    private String state;
    private String parentid;

    public String get_id() {
        return _id;
    }

    public void set_id(String _id) {
        this._id = _id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Date getPublishtime() {
        return publishtime;
    }

    public void setPublishtime(Date publishtime) {
        this.publishtime = publishtime;
    }

    public String getUserid() {
        return userid;
    }

    public void setUserid(String userid) {
        this.userid = userid;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public Integer getVisits() {
        return visits;
    }

    public void setVisits(Integer visits) {
        this.visits = visits;
    }

    public Integer getThumbup() {
        return thumbup;
    }

    public void setThumbup(Integer thumbup) {
        this.thumbup = thumbup;
    }

    public Integer getShare() {
        return share;
    }

    public void setShare(Integer share) {
        this.share = share;
    }

    public Integer getComment() {
        return comment;
    }

    public void setComment(Integer comment) {
        this.comment = comment;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getParentid() {
        return parentid;
    }

    public void setParentid(String parentid) {
        this.parentid = parentid;
    }
}

(2)建立資料訪問介面
com.tensquare.spit包下建立dao包,包下建立介面

package com.tensquare.search.spit.dao;

import com.tensquare.search.spit.pojo.Spit;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface SpitDao extends MongoRepository<Spit, String> {
    Page<Spit> findByParentid(String parentId, Pageable pageable);
}

(3)建立業務邏輯類
com.tensquare.spit包下建立service包,包下建立類

package com.tensquare.search.spit.service;

import com.tensquare.search.spit.dao.SpitDao;
import com.tensquare.search.spit.pojo.Spit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import util.IdWorker;

import java.util.List;

@Service
public class SpitService {
    @Autowired
    SpitDao spitDao;

    @Autowired
    IdWorker idWorker;

    @Autowired
    MongoTemplate mongoTemplate;

    public List<Spit> findAll() {
        return spitDao.findAll();
    }

    /*** 增加 * @param spit */

    public void add(Spit spit) {
        spit.set_id(String.valueOf(idWorker.nextId()));
        spitDao.save(spit);
    }

    /**
     * 修改
     * @param spit
     */
    public void update(Spit spit) {
        spitDao.save(spit);
    }

    /*** 刪除 * @param id */
    public void deleteById(String id) {
        spitDao.deleteById(id);
    }

    /**
     * 根據id查詢記錄
     * @param id
     * @return
     */
    public Spit findById(String id) {
       return spitDao.findById(id).get();
    }

    /**
     * 根據上級ID查詢吐槽列表(分頁)
     * @param parentId
     * @param page
     * @param size
     * @return
     */
    public Page<Spit> findByParentId(String parentId, int page, int size){
        PageRequest request = PageRequest.of(page-1, size);
        return spitDao.findByParentid(parentId, request);
    }
}

4.2.3 吐槽點贊

我們看一下以下點讚的程式碼: SpitService 新增updateThumbup方法

 /**
     * 吐槽點贊
     * @param id
     */
    public void updateThumbup(String id){
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        Update update = new Update();
        update.inc("thumbup", 1);
        mongoTemplate.updateFirst(query, update, "spit");
    }

4.2.4 控制不能重複點贊

我們可以通過redis控制使用者不能重複點贊
(1)首先引入依賴

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

修改application.yml, 在spring的節點下加;

 redis:
    host: 192.168.11.131

(3)修改SpitController程式碼邏輯

	@Autowired 
	private RedisTemplate redisTemplate;

    /**
     * 點贊
     *
     * @param spitId
     * @return
     */
    @RequestMapping(value = "/thumbup/{spitId}", method = RequestMethod.PUT)
    public Result updateThumbup(@PathVariable String spitId) {

        String userid = "2023";// 後邊我們會修改為當前登陸的使用者
        if(redisTemplate.opsForValue().get("thumbup_" + userid + "_" + spitId) != null){
            return new Result(false,StatusCode.REPERROR,"你已經點過讚了");
        }
        spitService.updateThumbup(spitId);
        return new Result(true, StatusCode.OK, "點贊成功");
    }

4.2.5 釋出吐槽

修改SpitService的add方法

   /*** 增加 * @param spit */

    public void add(Spit spit) {
        /*** 釋出吐槽(或吐槽評論) * @param spit */
        spit.set_id( idWorker.nextId()+"" );
        spit.setPublishtime(new Date());//釋出日期
        spit.setVisits(0);//瀏覽量
        spit.setShare(0);//分享數
        spit.setThumbup(0);//點贊數
        spit.setComment(0);//回覆數
        spit.setState("1");//狀態
        if(spit.getParentid()!=null && !"".equals(spit.getParentid())){// 如果存在上級ID,評論
            Query query=new Query();
            query.addCriteria(Criteria.where("_id").is(spit.getParentid()));
            Update update=new Update(); 
            update.inc("comment",1);
            mongoTemplate.updateFirst(query,update,"spit");
        }
        spitDao.save(spit);
    }

相關文章