第三章-文档型数据库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数据库的逻辑结构概念的对比

MongoDB MySql
数据库(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) 引入依赖
        
        <dependency>
            <groupId>org.mongodbgroupId>
            <artifactId>mongodb-driverartifactId>
            <version>3.6.3version>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.16.12version>
        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

字段名称 字段含义 字段类型 备注
_id ID 文本
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.bootgroupId>
            <artifactId>spring-boot-starter-data-mongodbartifactId>
    dependency>
    <dependency>
            <groupId>com.tensquaregroupId>
            <artifactId>tensquare-commonartifactId>
            <version>1.0-SNAPSHOTversion>
    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.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
    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

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

5.2 代码实现

5.2.1 新增评论

  • (1) 修改tensquare-article工程的 pom.xml
	  <dependency>
		  <groupId>org.springframework.bootgroupId>
		  <artifactId>spring-boot-starter-data-mongodbartifactId>
	  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, "删除成功");
    }

你可能感兴趣的:(十次方后台)