第三章-文档型数据库MongoDB

学习目标:

  • 理解MongoDB 的特点和体系结构
  • 掌握常用的MongoDB命令
  • 能够用java操作MongoDB
  • 使用SpringDataMongoDB完成吐槽微服务的开发

1.MongoDB的简介

1.1 吐槽和评论数据特点分析

对于下面这样的数据,我们更适合使用MongoDB来实现数据的存储

  1. 数据量大
  2. 写入操作频繁
  3. 价值较低

1.2 什么是MongoDB

MongoDB是一种跨平台的,面向文档的数据库,是当前NoSql数据库产品中最热门的一种,它介于关系型数据库和非关系型数据库之间,是非关系型数据库当中功能最丰富,最像关系型数据库的产品。它支持的数据结构非常松散,是类似与JSON的BSON格式,因此可以存储比较复杂的数据类型。官网地址 http://www.mongodb.org/

1.3 MongoDB的特点

MongoDB最大的特点在与是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现列斯与关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。他是一个面向集合的,模式自由的文档型数据库。

  • (1) 面向集合存储,易于存储对象类型的数据
  • (2) 模式自由
  • (3) 支持动态查询
  • (4) 支持完全索引,包含内部对象
  • (5) 支持复制和故障恢复
  • (6) 使用高效的二进制数据存储,包括大型对象(如视频等)
  • (7) 自动处理碎片,以支持云计算层次的扩展性
  • (8) 支持python,php,java,c,c++javascript,perl的驱动程序,社区也提供了对Erlang及.net等平台的驱动程序
  • (9) 文件存储格式为BSON(JSON的扩展)

1.4 MongoDB的体系结构

mongdb的逻辑结构是一种层次结构主要由
文档(document),集合(collection),数据库(database) 这三部分构成的。逻辑结构是面向用户的,用户使用MongoDB开发应用程序使用的就是逻辑结构。

  • (1) MongoDB 的文档(document),相当于关系数据库中的一行记录。
  • (2) 多个文档组成一个集合(collection),相当于关系数据库的表。
  • (3) 多个集合(collection),逻辑上组织在一起,就是数据库(database)。
  • (4) 一个 MongoDB 实例支持多个数据库(database)。

1.5 MongoDB与MySql数据库的逻辑结构概念的对比

MongoDBMySql
数据库(databases)数据库(databases)
集合(collections)表(table)
文档(document)行(row)

1.6 数据类型

  • 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

2.1 MogoDB的安装与启动

2.1.1 Windows 系统的MongoDB安装

mongoDB的默认端口是27017 如果我们想改变默认的启动端口,可以通过–port来指定端口

2.1.1.1 下载安装

下载安装 mongodb-win32-x86_64-2008plusssl-3.2.10-signed.msi 双击按照提示下一步即可,默认安装在
C:\Program Files\MongoDB 中。

2.1.1.2 启动
  • (1) 首先打开命令提示符,创建一个用于存放数据的目录
md d:\data
  • (2)启动服务
mongod --dbpath=d:\data
  • (3)登录
mongo
  • (4)退出
exit

Docker环境下安装MongoDB

  • (1) 检查docker 镜像在仓库中是否有
docker search mongo
  • (2) 拉取官方镜像
docker pull mongo
  • (3) 检查本地是否存在镜像了
docker images mongo
  • (4) 启动docker中的镜像
docker run -di --name=snow_mongo -p 27017:27017 mongo
  • (5) 启动docker中的镜像
docker run -di --name=snow_mongo -p 27017:27017 mongo
  • (6) 检查启动的情况
docker ps | grep snow_mongo
  • (7) 远程登录

这一步需要在宿主机安装mongo客户端,具体百度

mongo 114.67.156.73

2.1 MogoDB的常用命令

2.2.1 选择和创建数据库
  • (1) 选择和创建数据库
use 数据库名称

这里创建 spit 数据库,如果数据库不存在则自动创建,如果存在则是切换数据库

> use spitdb
switched to db spitdb
2.2.2 插入与查询文档

每条文档会有一个叫_id的字段,这个相当于我们原来关系数据库中表的主
键,当你在插入文档记录时没有指定该字段,MongoDB会自动创建,其类型是ObjectID
类型。如果我们在插入文档记录时指定该字段也可以,其类型可以是ObjectID类型,也
可以是MongoDB支持的任意类型。

  • (1): 插入文档的语法格式:

db.集合名称.insert(数据); 下面插入测试数据

db.spit.insert({content:"哇哈哈,是这个世界上最好的饮料",userid:"1011",nickname:"哇哈哈",visits:NumberInt(902)})
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)});
  • (2): 查询集合的语法格式:

db.集合名称.find(),默认是查询所有
开始测试

> db.spit.find()
{ "_id" : ObjectId("5c143012343bec2180f0e7b5"), "content" : "哇哈哈,是这个世界上最好的饮料", "userid" : "1011", "nickname" : "哇哈哈", "visits" : 902 }
{ "_id" : "1", "content" : "小姐姐,小姐姐是世界上最好看的,没有之一", "userid" : "1012", "nickname" : "小黄", "visits" : 2020 }
{ "_id" : "2", "content" : "上海市世界上我最不喜欢的城市.没有之一", "userid" : "1013", "nickname" : "下白", "visits" : 1023 }
{ "_id" : "3", "content" : "喜欢深圳,因为喜欢的人在深圳", "userid" : "1013", "nickname" : "小花", "visits" : 111 }
{ "_id" : "4", "content" : "坚持就是胜利", "userid" : "1014", "nickname" : "小青", "visits" : 1223 }
>
  • (3): 按照条件查询

比如我想查询userid为1013的记录,只
要在find()中添加参数即可,参数也是json格式,如下:

> db.spit.find({userid:'1013'})
{ "_id" : "2", "content" : "上海市世界上我最不喜欢的城市.没有之一", "userid" : "1013", "nickname" : "下白", "visits" : 1023 }
{ "_id" : "3", "content" : "喜欢深圳,因为喜欢的人在深圳", "userid" : "1013", "nickname" : "小花", "visits" : 111 }
>
  • (4): 只想查询一条记录
> db.spit.findOne({userid:'1013'})
{
        "_id" : "2",
        "content" : "上海市世界上我最不喜欢的城市.没有之一",
        "userid" : "1013",
        "nickname" : "下白",
        "visits" : 1023
}
>
  • (5): 返回指定行数的记录
> db.spit.find().limit(3)
{ "_id" : ObjectId("5c143012343bec2180f0e7b5"), "content" : "哇哈哈,是这个世界上最好的饮料", "userid" : "1011", "nickname" : "哇哈哈", "visits" : 902 }
{ "_id" : "1", "content" : "小姐姐,小姐姐是世界上最好看的,没有之一", "userid" : "1012", "nickname" : "小黄", "visits" : 2020 }
{ "_id" : "2", "content" : "上海市世界上我最不喜欢的城市.没有之一", "userid" : "1013", "nickname" : "下白", "visits" : 1023 }
>
2.2.3 修改文档

修改文档的语法结构:db.集合名称.update(条件,修改后的数据)

示例代码

> db.spit.find({_id:"1"}) // 修改之前先查询一次
{ "_id" : "1", "content" : "小姐姐,小姐姐是世界上最好看的,没有之一", "userid" : "1012", "nickname" : "小黄", "visits" : 2020 }
>
> db.spit.update({_id:"1"},{visits:NumberInt(7000)}) // 你好
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
> db.spit.find({_id:"1"}) // 修改之后再查询一次,对比下记录
{ "_id" : "1", "visits" : 7000 }
>

执行了上面代码之后,我们惊奇的发现了.除了被修改值的visits还在,其他的都没了 !!!
为了解决这个问题,我们需要使用修改器KaTeX parse error: Expected '}', got 'EOF' at end of input: …实现 把你要修改的参数用 `{set: { 修改的参数和值 K:V }}`

示例代码: 将错误的nickname修改为正确的

> db.spit.find({"_id":"2"}) 
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "下白", "visits" : 1023 }
> db.spit.update({_id:"2"},{$set:{nickname:"小白"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.spit.find({"_id":"2"})                       })
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
>
2.2.4 删除文档

删除文档的语法: db.集合名称.remove({条件}),如果条件为空db.spit.remove({})将删除所有的请注意

示例代码: 删除_id 为4的文档

> db.spit.find({"_id":"4"})
{ "_id" : "4", "content" : "坚持就是胜利", "userid" : "1014", "nickname" : "小青", "visits" : 1223 }
> db.spit.remove({_id:"4"})
WriteResult({ "nRemoved" : 1 })
> db.spit.find({"_id":"4"})
>
>
2.2.5 统计条数

统计记录条件使用count()方法

  • (1) 统计所有的
> db.spit.count()
4
>
  • (2) 统计符合要求的,这里匹配包含 “小姐姐” 字样的
>
> db.spit.count({content:/小姐姐/})
1
> db.spit.find({})
{ "_id" : ObjectId("5c143012343bec2180f0e7b5"), "content" : "哇哈哈,是这个世界上最好的饮料", "userid" : "1011", "nickname" : "哇哈哈", "visits" : 902 }
{ "_id" : "1", "visits" : 7000 }
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
{ "_id" : "3", "content" : "喜欢深圳,因为喜欢的人在深圳", "userid" : "1013", "nickname" : "小花", "visits" : 111 }
2.2.6 模糊匹配

模糊匹配是通过正则表达式实现的 格式为: /要匹配的字符串/ 其实在2.2.5我已经用过了

  • (1) 匹配某个字段内容包含 "小姐姐"字样的
> db.spit.find({content:/小姐姐/})
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
>
  • (2) 匹配某个字段内容一 “上海” 开始的
> db.spit.find({content:/^上海/}))
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
>
  • (3) 匹配某个字段内容以 “饮料” 结尾的
> db.spit.find({content:/饮料$/})
{ "_id" : ObjectId("5c143012343bec2180f0e7b5"), "content" : "哇哈哈,是这个世界上最好的饮料", "userid" : "1011", "nickname" : "哇哈哈", "visits" : 902 }
>
2.2.7 大于 小于 不等于

语法如下

  • 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}})
{ "_id" : "1", "visits" : 7000 }
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
2.2.8 包含与不包含
  • 包含

包含使用$in操作符。示例代码如下:

> db.spit.find({_id:{$in:["2","3"]}})
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
{ "_id" : "3", "content" : "喜欢深圳,因为喜欢的人在深圳", "userid" : "1013", "nickname" : "小花", "visits" : 111 }
>
  • 不包含

不包含使用$nin操作符

> db.spit.find({_id:{$nin:["2","3"]}})
{ "_id" : "1", "visits" : 7000 }
{ "_id" : ObjectId("5c143012343bec2180f0e7b5"), "content" : "哇哈哈,是这个世界上最好的饮料", "userid" : "1011", "nickname" : "哇哈哈", "visits" : 902 }
>
2.2.9 条件连接
  • and操作 $and:[ { },{ },{ } ]

我们如果需要查询同时满足两个以上条件,需要使用$and操作符将条件进行关联。

示例代码: 查询访问量 visits大于等于1000 并且小于2000的文档

> db.spit.find({$and:[ {visits:{$gte:1000}} ,{visits:{$lt:2000} }]})
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
>

// 生产中不能这么写,我只是为了试试语法是不是能正常解析这种而已
> db.spit.find({$and:[ {_id:{$in:["2","3"]}},{_id:"2"}]})
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
>
  • or操作 $or:[ { },{ },{ } ]

示例代码: 查询集合中userid为1013,或者浏览量小于2000的文档记录

> db.spit.find({$or:[ {userid:"1013"} ,{visits:{$lt:2000} }]})
{ "_id" : ObjectId("5c143012343bec2180f0e7b5"), "content" : "哇哈哈,是这个世界上最好的饮料", "userid" : "1011", "nickname" : "哇哈哈", "visits" : 902 }
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
{ "_id" : "3", "content" : "喜欢深圳,因为喜欢的人在深圳", "userid" : "1013", "nickname" : "小花", "visits" : 111 }
>
2.2.10 列值增长

如果我们想实现对某列值在原有值的基础上进行增加或减少,可以使用$inc运算符来实现

示例代码: 给_ID为2的用户访问量增加1

> db.spit.find({_id:"2"}) // 查询修改之前
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1023 }
>

> db.spit.update({_id:"2"},{$inc:{visits:NumberInt(1)}} )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.spit.find({_id:"2"}) // 查询修改之后
{ "_id" : "2", "content" : "上海是我最不喜欢的城市没有之一,小姐姐不在这里!", "userid" : "1013", "nickname" : "小白", "visits" : 1024 }
>

3. Java操作MongoDB

3.1 MongoDB 驱动

mongodb-driver是mongo官方推出的java连接mongoDB的驱动包,相当于JDBC驱动。

3.1.1 测试MONGODB, JAVA API准备工作

  • (1) 创建Maven公衡工程 mongoDemo
  • (2) 引入依赖
        <!--mongodb 驱动类-->
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver</artifactId>
            <version>3.6.3</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.12</version>
        </dependency>
  • (3) 新建一个测试类 MongoDemo 代码如下:
package com.snow.demo.mongo;

import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;


@Slf4j
public class MongoDemo {

    private MongoClient client = null;
    private MongoDatabase spitdb = null;
    private MongoCollection<Document> spit = null;
    private static final String MONGO_HOST = "114.67.156.73";
    private static final String MONGO_DATABASE_NAME = "spitdb";
    private static final String MONGO_COLLECTION_NAME = "spit";

    @Before
    public void before(){
        log.info("初始化资源 > > >  开始");
        client=new MongoClient(MONGO_HOST);//创建连接
        spitdb = client.getDatabase(MONGO_DATABASE_NAME);//打开数据库
        spit = spitdb.getCollection(MONGO_COLLECTION_NAME);//
        log.info("初始化资源 > > >  结束");
    }

    @After
    public void after(){
        log.info("关闭资源 > > > ");
        if (client != null){
            client.close();//关闭连接
        }
    }


    /**
     * 打印文档信息
     * @param documents
     */
    private void showDocuments(FindIterable<Document> documents){
        log.info("打印集合信息----开始 >>>>> ");
        for(Document document:documents){ //
            log.info("内容:【{}】,用户ID:【{}】,浏览量:【{}】",document.getString("content"),document.getString("userid"),document.getInteger("visits"));
        }
        log.info("打印集合信息----结束 >>>>> ");
    }

}

3.1.2 查询全部记录

    //查询所有
    @Test
    public void testFind(){
        //  获取集合
        FindIterable<Document> documents = spit.find();//查询记录获取文档集合
        showDocuments(documents);
    }

3.1.3 条件查询

查询所有和按照条件查询其实就是参数不同了而已,参数需要使用到 BasicDBObject 对象
BasicDBObject对象:表示一个具体的记录,BasicDBObject实现了DBObject,是keyvalue的数据结构,用起来和HashMap是基本一致的。

  • (1) 查询userId为1013的
    // 测试查询所有userId为1013的
    @Test
    public void testFindByUserID(){
        //  获取集合
        BasicDBObject paramter = new BasicDBObject("userid","1013");
        FindIterable<Document> documents = spit.find(paramter);//查询记录获取文档集合
        showDocuments(documents);
    }
  • (2) 查询浏览量大于1000的记录
    // 查询访问量大于1000的
    @Test
    public void testFindByVisitsGT(){
        //  获取集合
        BasicDBObject paramter = new BasicDBObject("visits", new BasicDBObject("$gt",1000));
        FindIterable<Document> documents = spit.find(paramter);//查询记录获取文档集合
        showDocuments(documents);
    }

3.1.4 插入数据

  • (1) : 执行插入语句
    // 查询访问量大于1000的
    @Test
    public void testInsertToMongo(){
        //  准备要插入的参数
        Map<String,Object> insertParamMap=new HashMap();
        insertParamMap.put("content","想小姐姐了.不知道小姐姐有没有每天好好吃饭,每天好好睡觉 ... ");
        insertParamMap.put("userid","1024");
        insertParamMap.put("visits",1024);
        insertParamMap.put("publishtime",new Date());
        
        // 构建文档对象
        Document document = new Document(insertParamMap);
        spit.insertOne(document);
    }
  • (2) : 插入之后去查询下,看看有没有了
>  db.spit.find({userid:"1024"})
{ "_id" : ObjectId("5c144faf04c7b30d1ce528f9"), "visits" : 1024, "publishtime" : ISODate("2018-12-15T00:49:51.143Z"), "userid" : "1024", "content" : "想小姐姐了.不知道小姐姐有没有每天好好吃饭,每天好好睡觉 ... " }
>

3.2 SpringDataMongoDB

SpringData家族成员之一,用于操作MongoDb的持久层框架,封装了底层的mongodbdriver。
官网主页: https://projects.spring.io/spring-data-mongodb/

4: 吐槽微服务

4.1 表结构分析

吐槽表 spit

字段名称字段含义字段类型备注
_idID文本
content吐槽内容文本
publishtime发布日期日期
userid发布人ID文本
nickname发布人昵称文本
visits浏览量整型
thumbup点赞数整型
share分享数整型
comment回复数整型
state是否可见文本
parentid上级ID文本

4.2 需求分析

采用SpringDataMongoDB框架实现吐槽微服务的持久层
要实现如下功能:

    • 基本增删改查API
    • 根据上级ID查询吐槽列表
    • 吐槽点赞
    • 发布吐槽

4.2 代码编写

4.2.1 模块搭建
  • (1)搭建子模块 tensquare-spit (拷贝mongo-demo,改名和修改pom文件即可)
  • (2)pom.xml引入依赖
     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
            <groupId>com.tensquare</groupId>
            <artifactId>tensquare-common</artifactId>
            <version>1.0-SNAPSHOT</version>
    </dependency>
  • (3) 创建application.yml
server:
 port: 9006
spring:
 application:
  name: tensquare‐spit #指定服务名
 data:
  mongodb:
   host: 114.67.156.73
   database: spitdb
  • (4)创建启动类
package com.tensquare.spit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import util.IdWorker;

@SpringBootApplication
public class SpitApplication {

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

	@Bean
	public IdWorker idWorkker(){
		return new IdWorker(1, 1);
	}
	
}
4.2.2 基本增删改查API实现
  • (1) 准备工作 pojo
package com.tensquare.spit.pojo;

import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;

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

@Data
@NoArgsConstructor
@Accessors(chain = true)
@Builder
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;
}

  • (2) 创建数据访问接口 dao

创建dao包,包下创建接口 SpitDao

package com.tensquare.spit.dao;

import com.tensquare.spit.pojo.Spit;
import org.springframework.data.mongodb.repository.MongoRepository;

/**
 * 吐槽数据访问层
 */
public interface SpitDao extends MongoRepository<Spit,String> {
}
  • (3) 创建业务逻辑类 service
package com.tensquare.spit.service;

import com.tensquare.spit.dao.SpitDao;
import com.tensquare.spit.pojo.Spit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import util.IdWorker;

import java.util.List;

@Service
public class SpitService {

    @Autowired
    private SpitDao spitDao;

    @Autowired
    private IdWorker idWorker;

    /**
     * 查询所有记录
     * @return
     */
    public List<Spit> findAll(){
        return spitDao.findAll();
    }


    /**
     * 根据逐渐Id查询
     * @param id
     * @return
     */
    public Spit findById(String id){
        Spit spit = spitDao.findById(id).get();
        return spit;
    }

    /**
     * 增加
     * @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);
    }


    /**
     * 根据ID删除
     * @param id
     */
    public void deleteById(String id){
        spitDao.deleteById(id);
    }
}
  • (4) 访问控制器实现 controller
package com.tensquare.spit.controller;

import com.tensquare.spit.pojo.Spit;
import com.tensquare.spit.service.SpitService;
import entity.Result;
import entity.StatusCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@CrossOrigin
@RequestMapping("/spit")
public class SpitController {

    @Autowired
    private SpitService spitService;

    //查询所有
    @GetMapping
    public Result findAll(){
        return new Result(true, StatusCode.OK,"查询成功",spitService.findAll());
    }

    //根据ID查询
    @GetMapping("/{id}")
    public Result findOne(@PathVariable String id){
        return new Result(true, StatusCode.OK,"查询成功",spitService.findById(id));
    }


    //增加
    @PostMapping
    public Result add(@RequestBody Spit spit ){
        spitService.add(spit);
        return new Result(true,StatusCode.OK,"增加成功");
    }


    //根据ID修改
    @PutMapping("/{id}")
    public Result update(@RequestBody Spit spit,@PathVariable String id )
    {
        spit.set_id(id);
        spitService.update(spit);
        return new Result(true,StatusCode.OK,"修改成功");
    }

    //根据ID删除
    @DeleteMapping("/{id}")
    public Result deleteById(@PathVariable String id ){
        spitService.deleteById(id);
        return new Result(true,StatusCode.OK,"删除成功");
    }
}

  • (5) 调试下

测试findAll即可,这里直接采用idea控制台命令curl操作. 如果你的控制台中文是乱码了.请尝试下面操作

  • 切换控制台编码格式为UTF-8 chcp 65001
  • 切换控制台编码格式为简体中文 chcp 936
  • 开始执行访问 curl http://localhost:9006/spit
C:\Users\snow\Desktop\tensquare-parent>curl http://localhost:9006/spit
{"flag":true,"code":20000,"message":"查询成功","data":[{"_id":"5c143012343bec2180f0e7b5","content":"哇哈哈,是这个世界上最好的饮料","publishtime":null,"userid":"1011","nickname":"哇哈哈","visits":902,"
thumbup":null,"share":null,"comment":null,"state":null,"parentid":null},{"_id":"1","content":null,"publishtime":null,"userid":null,"nickname":null,"visits":7000,"thumbup":null,"share":null,"comment":n
ull,"state":null,"parentid":null},{"_id":"2","content":"上海是我最不喜欢的城市没有之一,小姐姐不在这里!","publishtime":null,"userid":"1013","nickname":"小白","visits":1024,"thumbup":null,"share":null,"
comment":null,"state":null,"parentid":null},{"_id":"3","content":"喜欢深圳,因为喜欢的人在深圳","publishtime":null,"userid":"1013","nickname":"小花","visits":111,"thumbup":null,"share":null,"comment":n
ull,"state":null,"parentid":null},{"_id":"5c144faf04c7b30d1ce528f9","content":"想小姐姐了.不知道小姐姐有没有每天好好吃饭,每天好好睡觉 ... ","publishtime":"2018-12-15T00:49:51.143+0000","userid":"1024"
,"nickname":null,"visits":1024,"thumbup":null,"share":null,"comment":null,"state":null,"parentid":null}]}
C:\Users\snow\Desktop\tensquare-parent>

4.2.3 根据上级ID查询吐槽列表
  • (1) SpitDao新增方法定义
   /**
     * 根据上级ID查询吐槽列表(分页)
     * @param parentid
     * @param pageable
     * @return
     */
    Page<Spit> findByParentid(String parentid, Pageable pageable);
  • (2) SpitService新增方法
    /**
     * 根据上级ID查询吐槽列表
     * @param parentId
     * @param page
     * @param size
     * @return
     */
    public Page<Spit> findByParentId(String parentId, int page, int size){
        PageRequest pageRequest = PageRequest.of(page-1, size);
        return spitDao.findByParentid(parentId, pageRequest);
    }
  • (3) SpitController新增方法
    /**
     * 根据上级ID查询吐槽分页数据
     *
     * @param page
     * @param size
     * @return
     */
    @GetMapping("/comment/{parentId}/{page}/{size}")
    public Result findByParentid(@PathVariable String parentId,
                                 @PathVariable int page,
                                 @PathVariable int size) {
        Page<Spit> pageList = spitService.findByParentId(parentId, page, size);
        return new Result(true, StatusCode.OK, "查询成功", new
                PageResult<Spit>(pageList.getTotalElements(), pageList.getContent()));
    }
4.2.4 吐槽点赞
  • (1) SpitService新增方法
    /**
     * 点赞
     * @param id
     */
    public void updateThumbup2(String id){
        Spit spit = spitDao.findById(id).get();
        spit.setThumbup(spit.getThumbup()+1);
        spitDao.save(spit);
    }
  • (2) SpitController新增方法
    @PutMapping("/thumbup/{spitId}")
    public Result thumbup(@PathVariable String spitId){
        spitService.updateThumbup(spitId);
        return new Result(true,StatusCode.OK,"点赞成功");
    }
  • (3) SpitService中 updateThumbup方法优化

以上方法虽然实现起来比较简单,但是执行效率并不高,因为我只需要将点赞数加1就可
以了,没必要查询出所有字段修改后再更新所有字段

    @Autowired //引入mongoTemplate 的 api
    private MongoTemplate mongoTemplate;
    /**
     * 点赞
     * @param id
     */
    public void updateThumbup(String id){
        //使用原生mongoTemplate的api去操作,效率更高
        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.5 控制不能重复点赞

在点赞的时候我们可以通过redis来控制用户不能重复点赞

  • (1) 引入redis依赖
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
  • (2) 修改application.yml的配置文件
 redis:
  host: 127.0.0.1
  password: hbtrustRedis9527SC
  • (3) 在SpitController中增加 RedisTemplate 的引用
    @Autowired
    private RedisTemplate redisTemplate;
  • (4) 在SpitController中增加 RedisTemplate 的引用
    @Autowired
    private RedisTemplate redisTemplate;
  • (5) 在SpitController类中修改点赞 thumbup 方法
    @PutMapping("/thumbup/{spitId}")
    public Result thumbup(@PathVariable String spitId){
        //判断当前用户是否已经点赞,但是现在我们没有做认证,暂时先把userid写死
        String userid = "111";
        //判断当前用户是否已经点赞
        if(redisTemplate.opsForValue().get("thumbup_"+userid)!=null){
            return new Result(false, StatusCode.REP_ERROR, "不能重复点赞");
        }
        spitService.updateThumbup(spitId);
        redisTemplate.opsForValue().set("thumbup_"+userid, 1);
        return new Result(true, StatusCode.OK, "点赞成功");
    }
4.2.6 发布吐槽
  • (1) 修改 SpitService 中增加add方法
    /**
     * 增加
     * @param spit
     */
    public void add(Spit spit){
        spit.set_id(String.valueOf(idWorker.nextId()));
        spit.setPublishtime(new Date());//发布日期
        spit.setVisits(0);//浏览量
        spit.setShare(0);//分享数
        spit.setThumbup(0);//点赞数
        spit.setComment(0);//回复数
        spit.setState("1");//状态
        //如果当前添加的吐槽,有父节点,那么父节点的吐槽回复数要加一
        if(StringUtils.hasText(spit.getParentid())){
            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);
    }
4.2.7 增加浏览量和分享数
  • (1) SpitService 中增加一个公共的方法,用来自增加入一条记录中某个记录的值
    /**
     * 通过id修改一条数据中的某个key
     * @param id 指定记录的id
     * @param key 需要修改的key
     * @param collectionName 指定集合名称
     */
    private void incKeyById(String id,String key,String collectionName){
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        Update update = new Update();
        update.inc(key, 1);
        mongoTemplate.updateFirst(query, update, collectionName);
    }
  • (2) 修改 SpitService 中增加add方法
    /**
     * 增加
     * @param spit
     */
    public void add(Spit spit){
        spit.set_id(String.valueOf(idWorker.nextId()));
        spit.setPublishtime(new Date());//发布日期
        spit.setVisits(0);//浏览量
        spit.setShare(0);//分享数
        spit.setThumbup(0);//点赞数
        spit.setComment(0);//回复数
        spit.setState("1");//状态
        //如果当前添加的吐槽,有父节点,那么父节点的吐槽回复数要加一
        if(StringUtils.hasText(spit.getParentid())){
            incKeyById(spit.getParentid(),"comment","spit");
        }
        // 增加分享数
        if(spit.getShare() != null && spit.getShare() > 0){
            incKeyById(spit.get_id(),"share","spit");
        }

        // 增加浏览量
        if(spit.getVisits() != null && spit.getVisits() > 0){
            incKeyById(spit.get_id(),"visits","spit");
        }
        spitDao.save(spit);
    }

5.文章评论功能开发

5.1 表结构分析

专栏文章评论 comment

字段名称字段含义字段类型备注
_idID文本
articleid文章ID文本
content评论内容文本
userid评论人ID文本
parentid评论ID文本如果为0表示文章的顶级评论
publishdate评论日期日期

5.2 代码实现

5.2.1 新增评论
  • (1) 修改tensquare-article工程的 pom.xml
	  <dependency>
		  <groupId>org.springframework.boot</groupId>
		  <artifactId>spring-boot-starter-data-mongodb</artifactId>
	  </dependency>
  • (2) 修改application.yml ,在spring节点下新增配置
 data:
  mongodb:
   host: 114.67.156.73
   database: spitdb
  • (3) 创建实体类
package com.tensquare.article.pojo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.annotation.Id;

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


@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Builder
@Slf4j
public class Comment implements Serializable {
    @Id
    private String _id;
    private String articleid;
    private String content;
    private String userid;
    private String parentid;
    private Date publishdate;
}
  • (4) 创建数据访问接口
package com.tensquare.article.dao;

import com.tensquare.article.pojo.Comment;
import org.springframework.data.mongodb.repository.MongoRepository;

/**
 * 评论DAO
 */
public interface CommentDao extends MongoRepository<Comment,String> {
    
}

  • (5) 创建业务逻辑类
package com.tensquare.article.service;

import com.tensquare.article.dao.CommentDao;
import com.tensquare.article.pojo.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import util.IdWorker;

@Service
public class CommentService {
    @Autowired
    private CommentDao commentDao;
    @Autowired
    private IdWorker idWorker;

    /**
     * 增加评论
     * @param comment
     */
    public void add(Comment comment){
        comment.set_id( String.valueOf(idWorker.nextId()));
        commentDao.save(comment);
    }
}
  • (6) 创建控制器类
package com.tensquare.article.controller;

import com.tensquare.article.pojo.Comment;
import com.tensquare.article.service.CommentService;
import entity.Result;
import entity.StatusCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@CrossOrigin
@RequestMapping("/comment")
public class CommentController {
    @Autowired
    private CommentService commentService;
    
    
    @PostMapping
    public Result save(@RequestBody Comment comment){
        commentService.add(comment);
        return new Result(true, StatusCode.OK, "提交成功 ");
    }
}
5.2.2 根据文章ID查询评论列表
  • (1) CommenDao 中增加方法
    /**
    * 根据文章ID查询评论列表
    * @param articleid
    * @return
    */
    public List<Comment> findByArticleid(String articleid);
  • (2) CommenService 中增加方法
    /**
     * 根据文章ID查询评论列表
     * @param articleid 文章id
     * @return
     */
    public List<Comment> findByArticleid(String articleid){
        return commentDao.findByArticleid(articleid);
    }
  • (3) CommenController 中增加方法
    @GetMapping("/article/{articleid}")
    public Result findByArticleid(@PathVariable String articleid){
        return new Result(true, StatusCode.OK, "查询成功",
                commentService.findByArticleid(articleid));
    }
5.2.3 删除评论
  • (1) CommenService 中增加方法
    /**
     * 根据ID删除评论
     * @param id
     */
    public void deleteById(String id){
        commentDao.deleteById(id);
    }
  • (2) CommenController 中增加方法
    @DeleteMapping("/{id}")
    public Result deleteById(@PathVariable String id) {
        commentService.deleteById(id);
        return new Result(true, StatusCode.OK, "删除成功");
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值