mongdb 数据库操作 (后边结合项目没什么用,前边是基础操作)

分享 123456789987654321 ⋅ 于 2020-07-01 21:06:33 ⋅ 最后回复由 青牛 2020-07-01 22:07:41 ⋅ 2064 阅读

第2章 MongoDB和文章评论管理

学习目标:

  • 了解什么是MongoDB
  • 掌握MongoDB的安装
  • 掌握MongoDB的常用命令
  • 掌握mongodb-driver的基本使用
  • 掌握SpringDataMongoDB的使用
  • 能够实现文章评论功能开发

1 MongoDB简介

1.1 什么是MongoDB

MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。

在高负载的情况下,添加更多的节点,可以保证服务器性能。

MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。

image-20191219164634949

Bson与Json区别:

(1)更快的遍历速度
对JSON格式来说,太大的JSON结构会导致数据遍历非常慢。在JSON中,要跳过一个文档进行数据读取,需要对此文档进行扫描才行,需要进行麻烦的数据结构匹配,比如括号的匹配,而BSON对JSON的一大改进就是,它会将JSON的每一个元素的长度存在元素的头部,这样你只需要读取到元素长度就能直接seek到指定的点上进行读取了。
(2)操作更简易
对JSON来说,数据存储是无类型的,比如你要修改基本一个值,从9到10,由于从一个字符变成了两个,所以可能其后面的所有内容都需要往后移一位才可以。而使用BSON,你可以指定这个列为数字列,那么无论数字从9长到10还是100,我们都只是在存储数字的那一位上进行修改,不会导致数据总长变大。当然,在MongoDB中,如果数字从整形增大到长整型,还是会导致数据总长变大的。
(3)增加了额外的数据类型
JSON是一个很方便的数据交换格式,但是其类型比较有限。BSON在其基础上增加了“byte array”数据类型。这使得二进制的存储不再需要先base64转换后再存成JSON。大大减少了计算开销和数据大小。

1.2 MongoDB主要特点

  • MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。

  • 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName="Sameer",Address="8 Gandhi Road")来实现更快的排序。

  • 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。

  • 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上,这就是所谓的分片。

  • Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。

  • MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。

  • Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。

  • Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。

  • Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。

  • GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。

  • MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。

  • MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。

  • MongoDB安装简单。

1.3 MongoDB使用场景

对于MongoDB实际应用来讲,是否使用MongoDB需要根据项目的特定特点进行甄别,这就需要我们对MongoDB适用和不适用的场景有一定的了解。

1.3.1 MongoDB 适用场景

1.网站实时数据:mongoDB非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。

2.数据缓存:由于性能很高,MongoDB 也适合作为信息基础设施的缓存层。在系统重启之后,由MongoDB
搭建的持久化缓存层可以避免下层的数据源过载。

3.大尺寸、低价值数据存储:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很
多时候程序员往往会选择传统的文件进行存储。

4.高伸缩性场景:MongoDB 非常适合由数十或数百台服务器组成的数据库。MongoDB 的路线图中已经包
含对MapReduce 引擎的内置支持。

5.对象或JSON 数据存储:MongoDB 的BSON 数据格式非常适合文档化格式的存储及查询。

1.3.2 不适用场景

1.高度事务性系统:例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂
事务的应用程序。

2.传统的商业智能应用:针对特定问题的BI 数据库会对产生高度优化的查询方式。对于此类应用,
数据仓库可能是更合适的选择。

3.需要复杂SQL 查询的问题。

1.4 MongoDB体系结构

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

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

文档(document)、集合(collection)、数据库(database)的层次结构如下图:

1559303526465

SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary key 主键,MongoDB自动将_id字段设置为主键

image-20200112105631078

1.5 MongoDB数据类型

数据类型 描述
String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean 布尔值。用于存储布尔值(真/假)。
Double 双精度浮点值。用于存储浮点值。
Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array 用于将数组或列表或多个值存储为一个键。
Timestamp 时间戳。记录文档修改或添加的具体时间。
Object 用于内嵌文档。
Null 用于创建空值。
Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID 对象 ID。用于创建文档的 ID。
Binary Data 二进制数据。用于存储二进制数据。
Code 代码类型。用于在文档中存储 JavaScript 代码。
Regular expression 正则表达式类型。用于存储正则表达式。

下面说明下几种重要的数据类型。

1.5.1 ObjectId

ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:

  • 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
  • 接下来的 3 个字节是机器标识码
  • 紧接的两个字节由进程 id 组成 PID
  • 最后三个字节是随机数

img

MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象

由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:

> var newObject = ObjectId()
> newObject.getTimestamp()
ISODate("2017-11-25T07:21:10Z")

ObjectId 转为字符串

> newObject.str
5a1919e63df83ce79df8b38f

1.5.2 时间戳

BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的 日期 类型不相关。 时间戳值是一个 64 位的值。其中:

  • 前32位是一个 time_t 值(与Unix新纪元相差的秒数)
  • 后32位是在某秒中操作的一个递增的序数

在单个 mongod 实例中,时间戳值通常是唯一的。

1.5.3 日期

表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期。

> var mydate1 = new Date()     //格林尼治时间
> mydate1
ISODate("2018-03-04T14:58:51.233Z")

当前时区

> Date()
Thu Dec 19 2019 17:01:57 GMT+0800

2 MongoDB安装

2.1 Windows安装

2.1.1 下载安装

MongoDB 提供了可用于 32 位和 64 位系统的预编译二进制包,你可以从MongoDB官网下载安装,MongoDB 预编译二进制包下载地址:https://www.mongodb.com/download-center/community

image-20191219170925235

根据你的系统下载 32 位或 64 位的 .msi 文件,下载后双击该文件,按操作提示安装即可。

安装过程中会提示是否将MongoDB安装成Windows服务,可以自行选择。

安装过程中 "install mongoDB compass" 可以不勾选(当然你也可以选择安装它,可能需要更久的安装时间),MongoDB Compass 是一个图形界面管理工具,我们可以在后面自己到官网下载安装,下载地址:https://www.mongodb.com/download-center/compass

2.1.2 创建数据目录

创建一个文件夹c:\data\db,用于存放数据的目录data

2.1.3 命令行下运行MongoDB

mongod --dbpath c:\data\db

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

mongod --dbpath=C:\data\db -port 8989

2.1.4 连接MongoDB

mongo 127.0.0.1:27017

如果ip是本地服务,端口号是27017,则后面的127.0.0.1:27017可以省略

2.2 Docker 中安装MongoDB

拉取镜像

docker pull mongo

运行容器

docker run -d --name=mongo -p 27017:27017 mongo

运行已创建容器

docker start [容器名或ID]

2.3 MongoDB 可视化工具

  • RockMongo
  • MongoDB Compass Community -- 官方提供
  • NoSQLBooster
  • Navicat for MongoDB

3 MongoDB命令

3.1 查看、选择及删除数据库

查看数据库:

show dbs

选择数据库,如果数据库不存在则自动创建

use commentdb

在往commentdb插入数据之前,调用show dbs并不会显示新创建的库。

数据库通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。

  • 不能是空字符串("")。
  • 不得含有' '(空格)、.、$、/、\和\0 (空字符)。
  • 应全部小写。
  • 最多64字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。

  • admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
  • local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
  • config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

查看当前数据库

db

删除数据库:

db.dropDatabase()

3.2 查看、选择及删除集合

查看已有集合,可以使用 show collectionsshow tables 命令:

show collections

创建集合

语法格式:

db.createCollection(name, options)

参数说明:

  • name: 要创建的集合名称
  • options: 可选参数, 指定有关内存大小及索引的选项

options 可以是如下参数:

字段 类型 描述
capped 布尔 (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数。
autoIndexId 布尔 (可选)如为 true,自动在 _id 字段创建索引。默认为 false。
size 数值 (可选)为固定集合指定一个最大值,以千字节计(KB)。 如果 capped 为 true,也需要指定该字段。
max 数值 (可选)指定固定集合中包含文档的最大数量。
db.createCollection("comment")

在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。

db.article.insert({_id:"1",articleId:"001",content:"说得很有道理",userId:"101",parentId:"0",publishDate:"2019-12-19 16:06",thumbUp:1000})

删除集合

db.collection.drop()

例如

db.article.drop()

3.3 插入文档

语法格式

db.COLLECTION_NAME.insert(document)

插入文档

db.article.insert({content:"十次方课程",userid:"1011"})

查询文档

db.comment.find()

可以看到MongoDB自动生成了类型为ObjectId的字段_id

插入测试数据

db.comment.insert({_id:"1",articleId:"001",content:"说得很有道理",userId:"101",parentId:"0",publishDate:new Date(),thumbUp:1000});
db.comment.insert({_id:"2",articleId:"001",content:"楼主太有才了",userId:"102",parentId:"0",publishDate:new Date(),thumbUp:1100});
db.comment.insert({_id:"3",articleId:"001",content:"这个可以啊",userId:"103",parentId:"0",publishDate:new Date(),thumbUp:1200});
db.comment.insert({_id:"4",articleId:"002",content:"我怀疑你在开车,可我没有证据",userId:"102",parentId:"0",publishDate:new Date(),thumbUp:1300});
db.comment.insert({_id:"5",articleId:"002",content:"同上",userId:"104",parentId:"0",publishDate:new Date(),thumbUp:1400});
db.comment.insert({_id:"6",articleId:"002",content:"这个有内涵",userId:"101",parentId:"0",publishDate:new Date(),thumbUp:1500});
db.comment.insert({_id:"7",articleId:"003",content:"理论上可行",userId:"103",parentId:"0",publishDate:new Date(),thumbUp:1600});

3.4 查询文档

查询语法

db.collection.find(query, projection)
  • query :可选,使用查询操作符指定查询条件

  • projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。

如果你需要以易读的方式来读取数据,语法格式如下:

db.comment.find()

如果想返回指定的字段

db.comment.find({},{_id:1})

MongoDB 与 RDBMS Where 语句比较

操作 格式 范例 RDBMS中的类似语句
等于 {:} db.col.find({"userId":"101"}) where userId= '101'
小于 {:{$lt:}} db.col.find({"thumpUp":{$lt:50}}) where thumpUp < 50
小于或等于 {:{$lte:}} db.col.find({"thumpUp":{$lte:50}}) where thumpUp <= 50
大于 {:{$gt:}} db.col.find({"thumpUp":{$gt:50}}) where thumpUp > 50
大于或等于 {:{$gte:}} db.col.find({"thumpUp":{$gte:50}}) where thumpUp >= 50
不等于 {:{$ne:}} db.col.find({"thumpUp":{$ne:50}}) where thumpUp != 50

包含使用$in操作符

查询评论集合中userid字段包含101和102的文档:

db.comment.find({userId:{$in:["101","102"]}})

不包含使用$nin操作符

查询评论集合中userid字段不包含101和102的文档:

db.comment.find({userId:{$nin:["101","102"]}})

And 条件

db.col.find({key1:value1, key2:value2})

Or 条件

db.col.find(
   {
      $or: [
         {key1: value1}, {key2:value2}
      ]
   }
)

And 和 Or 联合使用

类似常规 SQL 语句为: 'where thumbUp>1000 AND (userId = '101' OR articleId = '001')'

db.col.find({"thumbUp": {$gt:50}, $or: [{"userId": "101"},{"articleId": "001"}]})

limit()方法

db.COLLECTION_NAME.find().limit(NUMBER)

skip() 方法

db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

sort() 方法

db.COLLECTION_NAME.find().sort({KEY:1})

其中 1 为升序排列,而 -1 是用于降序排列。

db.comment.find().sort({"thumbUp":-1})

count() 方法

db.comment.count(query, options)

模糊查询

db.comment.find({content:/理/})
db.comment.find({content:/^理/})
db.comment.find({content:/理$/})
范例 RDBMS中的类似语句
db.comment.find({content:/理/}) where content like '%理%'
db.comment.find({content:/^理/}) where content like '理%'
db.comment.find({content:/理$/}) where content like '%理'

3.5 修改与删除文档

update() 方法

db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

参数说明:

  • query : update的查询条件,类似sql update查询内where后面的。
  • update : update的对象和一些更新的操作符(如set$,$inc...)等,也可以理解为sql update查询内set后面的
  • upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
  • multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
  • writeConcern :可选,抛出异常的级别。

修改单条数据

db.comment.update({"userId":"104"}, {$set:{"thumbUp":1401}})

修改多条数据

db.comment.update({"userId":"101"}, {$set:{"publishDate":"2019-12-19 17:06"}},{multi:true})

列值增长

db.comment.update({"userId":"101"}, {$inc:{"thumbUp":2}},{multi:true})

删除列

db.comment.update({},{$unset:{"thumpUp":1}},{multi:true})

删除行

remove() 方法

db.collection.remove(
   <query>,
   {
     justOne: <boolean>,
     writeConcern: <document>
   }
)

参数说明:

  • query :(可选)删除的文档的条件。

  • justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。

  • writeConcern :(可选)抛出异常的级别。
db.comment.remove({_id:"1"})

4 MongoDB Java Driver

http://mongodb.github.io/mongo-java-driver/

4.1 添加引用

<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.12.0</version>
</dependency>

4.2 查询

public class ArticleTests {
    static MongoClient client;
    static MongoCollection<Document> comment;
    @BeforeAll
    public static void connectMongo(){
        //创建连接
        client = new MongoClient("127.0.0.1");
        //打开数据库
        MongoDatabase commentdb = client.getDatabase("commentdb");
        //获取集合
       comment = commentdb.getCollection("comment");
    }

    @AfterAll
    public static void closeMongo(){
        //关闭连接
        client.close();
    }

    @Test
    public void testFind(){
        //查询
        FindIterable<Document> documents = comment.find();
        //查询记录获取文档集合
        for (Document document : documents) {
            System.out.println("_id:" + document.get("_id"));
            System.out.println("内容:" + document.get("content"));
            System.out.println("用户ID:" + document.get("userId"));
            System.out.println("点赞数:" + document.get("thumbUp"));
        }
    }

    @Test
    public void testFindOne(){
        //查询
        Bson bson = new BasicDBObject("_id","5");
        FindIterable<Document> documents = comment.find(bson);
        //查询记录获取文档集合
        for (Document document : documents) {
            System.out.println("_id:" + document.get("_id"));
            System.out.println("内容:" + document.get("content"));
            System.out.println("用户ID:" + document.get("userId"));
            System.out.println("点赞数:" + document.get("thumbUp"));
        }
    }
    //条件查询
    @Test
    public void find(){
        Bson bson = new BasicDBObject();
        ((BasicDBObject) bson).append("userId","102");
        ((BasicDBObject) bson).append("articleId","002");
        FindIterable<Document> documents = comment.find(bson);
        showData(documents);
    }
}
//复杂查询--使用Filters
    @Test
    public void findByMap(){
//        Document document = new Document();
//        document.append("userId","102");
//        document.append("content","/有/");
//        document.append("articleId","002");
//        document.append("thumbUp",new Document("$gt",1200));
//        FindIterable<Document> documents = collection.find(document);
        FindIterable<Document> documents = null;
//        documents = collection.find(and(gt("thumbUp",1200),eq("userId","102")));
//        Pattern pattern = null;
//        pattern = Pattern.compile(".*理.*");
//        pattern = Pattern.compile("^理.*");
//        pattern = Pattern.compile(".*理$");
        documents = collection.find(Filters.regex("content",".*理.*"));
//        documents = collection.find(Filters.regex("content","^理.*"));
//        documents = collection.find(Filters.regex("content",".*理$"));
        for(Document d : documents){
            System.out.println(d.get("_id")+" "+d.get("articleId")+" "+d.get("content")+" "+d.get("thumbUp"));
        }
    }

4.3 新增

    @Test
    public void add(){
        Map<String, Object> map = new HashMap<>();
        map.put("_id","7");
        map.put("articleId","003");
        map.put("content","黑马程序员");
        map.put("userId","105");
        map.put("parentId","6");
        map.put("publishDate",new Date());
        map.put("thumbUp",0);
        Document document = new Document(map);
        comment.insertOne(document);
    }

4.4 修改

    @Test
    public void update(){
        Bson bson = new BasicDBObject("_id","6");
        Bson bson1 = new BasicDBObject("$set", new Document("thumbUp", 2000));
        comment.updateOne(bson,bson1);
    }
    @Test
    public void inc(){
        Bson bson = new BasicDBObject("_id","6");
        Bson bson1 = new BasicDBObject("$inc", new Document("thumbUp", 1));
        comment.updateOne(bson,bson1);
    }
    // 更新
    @Test
    public void update(){
        Bson query = new BasicDBObject();
        ((BasicDBObject) query).append("userId","102");
        Bson update = new BasicDBObject();
        ((BasicDBObject) update).append("$inc",new Document("thumbUp",1));
        ((BasicDBObject) update).append("$set",new Document("publishDate",new Date()));
        comment.updateMany(query,update);
    }

使用Updates

    @Test
    public void update(){
        Bson filter = new Document();
//        ((Document) filter).append("_id","14");
        filter = Filters.eq("_id","14");
        Bson update = new Document();
//        ((Document) update).append("$set",new Documenst("thumbUp",1));
//        update = Updates.set("thumbUp",2);
        update = Updates.combine(Updates.set("thumbUp",3),Updates.set("parentId","13"));
        collection.updateOne(filter,update);
    }

4.5 删除

    @Test
    public void delete(){
        Bson bson = new BasicDBObject("_id","7");
        comment.deleteOne(bson);
    }
    @Test
    public void delete(){
        collection.deleteOne(Filters.eq("_id","14"));
    }

5 Spring Data MongoDB 与文章评论功能

5.1 需求分析

image-20191220090417156

image-20191220090621273

设计表结构:

专栏文章评论 comment
字段名称 字段含义 字段类型 备注
_id ID 文本
articleId 文章ID 文本
content 评论内容 文本
userId 评论人ID 文本
parentId 评论ID 文本 如果为0表示文章的顶级评论
publishDate 评论日期 日期
thumbUp 点赞数 数字

需要实现以下功能:

  1. 评论基本增删改查API
  2. 根据文章id查询评论
  3. 评论点赞

Spring Data MongoDB是SpringData家族成员之一,用于操作MongoDb的持久层框架,封装了底层的mongodb-driver。本功能使用SpringDataMongoDB进行开发。

5.2 添加Spring Data MongoDB

添加依赖

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

添加配置

spring:
  data:
    mongodb:
      host: 127.0.0.1
      port: 27017
      database: commentdb

5.3 增删改查API

添加实体类,注意这里的@Id

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(description="评论")
public class Comment implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @ApiModelProperty(value = "ID")
    private String id;

    @ApiModelProperty(value = "文章ID")
    private String articleId;

    @ApiModelProperty(value = "用户ID")
    private String userId;

    @ApiModelProperty(value = "父评论ID")
    private String parentId;

    @ApiModelProperty(value = "评论内容")
    private String content;

    @ApiModelProperty(value = "发表日期")
    private Date publishDate;

    @ApiModelProperty(value = "点赞数")
    private Integer thumbUp;

}

在com.tensquare.article.repository包中编写CommentRepository,注意不要和MyBatis的接口放在一个包

public interface ICommentReposiroty extends MongoRepository<Comment,String> {
    /**
     * 根据文章编号获取评论
     * @param articleId
     * @return
     */
    List<Comment> findByArticleId(String articleId);
}

新增CommentController

@RestController
@RequestMapping("/comment")
@Api(value = "文章评论接口",tags = "文章评论接口")
@CrossOrigin
public class CommentController {
    @Autowired
    ICommentReposiroty commentReposiroty;
    @Autowired
    MongoTemplate mongoTemplate;

    @ApiOperation(value = "新增文章评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "comment", value = "文章评论", required = true, dataType = "Comment")
    @PostMapping
    public Result save(@RequestBody Comment comment){
        comment.setPublishDate(new Date());
        commentReposiroty.insert(comment);
        return Result.createSuccess();
    }
    @ApiOperation(value = "更新文章评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "comment", value = "文章评论", required = true, dataType = "Comment")
    @PutMapping
    public Result update(@RequestBody Comment comment){
        comment.setPublishDate(new Date());
        commentReposiroty.save(comment);
        return Result.createSuccess();
    }
    @ApiOperation(value = "根据评论编号获取文章评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "id", value = "评论编号", required = true, dataType = "String")
    @GetMapping("/{id}")
    public Result<Comment> get(@PathVariable String id){
        Comment comment = commentReposiroty.findById(id).get();
        return Result.createSuccess(comment);
    }
    @ApiOperation(value = "获取所有评论", notes = "Author: mcm")
    @GetMapping
    public Result<List<Comment>> list(){
        List<Comment> comments = commentReposiroty.findAll();
        return Result.createSuccess(comments);
    }
    @ApiOperation(value = "查询文章ID查询评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "articleID", value = "文章ID", required = true, dataType = "String")
    @GetMapping("/article/{articleID}")
    public Result<List<Comment>> listByMap(@PathVariable String articleID){
        List<Comment> comments =  commentReposiroty.findByArticleId(articleID);
        return Result.createSuccess(comments);
    }
    @ApiOperation(value = "删除文章评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "id", value = "ID", required = true, dataType = "String")
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable String id){
        commentReposiroty.deleteById(id);
        return  Result.createSuccess();
    }
}

5.4 使用MongoTemplate

@RestController
@RequestMapping("/comment")
@Api(value = "评论接口",tags = "评论接口")
@CrossOrigin
public class CommentController {
    @Autowired
    MongoTemplate mongoTemplate;

    @ApiOperation(value = "新增评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "comment", value = "文章评论", required = true, dataType = "Comment")
    @PostMapping
    public Result save(@RequestBody Comment comment){
        comment.setPublishDate(new Date());
        mongoTemplate.insert(comment);
        return Result.createSuccess();
    }
    @ApiOperation(value = "更新评论", notes = "Author: mcm")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "id", value = "文章评论ID", required = true, dataType = "String"),
            @ApiImplicitParam(name = "map", value = "文章评论属性", required = true, dataType = "Comment")
    })
    @PutMapping("/{id}")
    public Result update(@PathVariable String id, @RequestBody Map<String,Object> map){
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        Update update = new Update();
        for(Map.Entry<String,Object> entry : map.entrySet()){
            update.set(entry.getKey(),entry.getValue());
        }
        mongoTemplate.updateMulti(query, update,Comment.class);
        return Result.createSuccess();
    }
    @ApiOperation(value = "根据评论编号获取评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "id", value = "评论编号", required = true, dataType = "String")
    @GetMapping("/{id}")
    public Result<Comment> get(@PathVariable String id){
        Comment comment = mongoTemplate.findById(id,Comment.class);
        return Result.createSuccess(comment);
    }
    @ApiOperation(value = "获取所有评论", notes = "Author: mcm")
    @GetMapping
    public Result<List<Comment>> list(){
        List<Comment> comments = mongoTemplate.findAll(Comment.class);
        return Result.createSuccess(comments);
    }
    @ApiOperation(value = "根据文章ID查询评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "articleID", value = "文章ID", required = true, dataType = "String")
    @GetMapping("/article/{articleID}")
    public Result<List<Comment>> findByArticleId(@PathVariable String articleID){
        Query query = new Query();
        query.addCriteria(Criteria.where("articleId").is(articleID));
        List<Comment> comments =  mongoTemplate.find(query,Comment.class);
        return Result.createSuccess(comments);
    }
    @ApiOperation(value = "查询评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "map", value = "文章属性", required = true, dataType = "Comment")
    @PostMapping("/search")
    public Result<List<Comment>> search(@RequestBody Map<String,Object> map){
        Query query = new Query();
        for(Map.Entry<String,Object> entry : map.entrySet()){
            query.addCriteria(Criteria.where(entry.getKey()).is(entry.getValue()));
        }
        List<Comment> comments = mongoTemplate.find(query,Comment.class);
        return Result.createSuccess(comments);
    }
    @ApiOperation(value = "分页查询评论", notes = "Author: mcm")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "current", value = "当前页", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "size", value = "每页条数", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "map", value = "文章属性", required = true, dataType = "Comment")
    })
    @PostMapping("/search/{current}-{size}")
    public Result<PageResult<Comment>> searchByPage(@PathVariable Long current, @PathVariable Integer size,@RequestBody Map<String,Object> map){
        Query query = new Query();
        for(Map.Entry<String,Object> entry : map.entrySet()){
            query.addCriteria(Criteria.where(entry.getKey()).is(entry.getValue()));
        }
        Long count = mongoTemplate.count(query,Comment.class);
        query.limit(size);
        query.skip((current-1)*size);
        List<Comment> comments = mongoTemplate.find(query,Comment.class);
        return Result.createSuccess(new PageResult<>(count,comments));
    }
    @ApiOperation(value = "删除评论", notes = "Author: mcm")
    @ApiImplicitParam(name = "id", value = "ID", required = true, dataType = "String")
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable String id){
        mongoTemplate.remove(Query.query(Criteria.where("_id").is(id)),Comment.class);
        return  Result.createSuccess();
    }
}

5.5 评论点赞

    @ApiOperation(value = "点赞", notes = "Author: mcm")
    @ApiImplicitParam(name = "commentId", value = "评论ID", required = true, dataType = "String")
    @PutMapping("/thumbup/{commentId}")
    public Result thumpUp(@PathVariable String commentId){
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(commentId));
        Update update = new Update();
        update.inc("thumbUp");
        mongoTemplate.updateFirst(query,update,Comment.class);
        return  Result.createSuccess();
    }

6 Spring Data Redis 与限制重复点赞

Redis是一个分布式内存型NOSQL数据库,支持每秒10W+读写,做集群部署。应用场景:短信验证码。

6.1 需求分析

同一用户对同一篇文章或者对同一个评论不能重复点赞

6.2 准备Redis

Docker中拉取redis镜像

docker pull redis

启动redis

docker run -d --name=1024_redis -p 6379:6379 redis

Redis 可视化工具

RedisDesktopManager

AnotherRedisDesktopManager 下载地址:https://gitee.com/qishibo/AnotherRedisDesktopManager

Medis

6.3 添加Spring Data Redis

添加依赖

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

添加配置

spring:
  redis:
    host: 192.168.200.128

在Controller中使用RedisTemplate

    @Autowired
    StringRedisTemplate redisTemplate;

添加Redis Key

    // 部门:业务:模块:键值
    private static final String REDIS_COMMENT_KEY = "dp1:pro1:article:comment";

修改点赞方法

@ApiOperation(value = "点赞", notes = "Author: mcm")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "commentId", value = "评论ID", required = true, dataType = "String"),
            @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "String")
    })
    @PutMapping("/thumbup/{userId}-{commentId}")
    public Result thumpUp(@PathVariable String commentId,@PathVariable String userId){
        if(redisTemplate.opsForSet().isMember(REDIS_COMMENT_KEY,userId + "_" + commentId)){
            return  Result.createFailure("您已经点过赞了~~");
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(commentId));
        Update update = new Update();
        update.inc("thumbUp");
        mongoTemplate.updateFirst(query,update,Comment.class);
        redisTemplate.opsForSet().add(REDIS_COMMENT_KEY,userId + "_" + commentId);
        return  Result.createSuccess();
    }
版权声明:原创作品,允许转载,转载时务必以超链接的形式表明出处和作者信息。否则将追究法律责任。来自海汼部落-123456789987654321,http://hainiubl.com/topics/75172
点赞
成为第一个点赞的人吧 :bowtie:
回复数量: 1
  • 青牛 海汼部落创始人,80后程序员一枚,曾就职于金山,喜欢倒腾技术做产品
    2020-07-01 22:07:41

    不错,可以领红包了,可惜扫不上了。第一张图挂了,修复一下。

暂无评论~~
  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,可用Emoji的自动补全, 在输入的时候只需要 ":" 就可以自动提示了 :metal: :point_right: 表情列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif,教程
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
Ctrl+Enter