第2章 MongoDB和文章评论管理
学习目标:
- 了解什么是MongoDB
- 掌握MongoDB的安装
- 掌握MongoDB的常用命令
- 掌握mongodb-driver的基本使用
- 掌握SpringDataMongoDB的使用
- 能够实现文章评论功能开发
1 MongoDB简介
1.1 什么是MongoDB
MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。
在高负载的情况下,添加更多的节点,可以保证服务器性能。
MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
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 开发应用程序使用的就是逻辑结构。
- MongoDB 的文档(document),相当于关系数据库中的一行记录。
- 多个文档组成一个集合(collection),相当于关系数据库的表。
- 多个集合(collection),逻辑上组织在一起,就是数据库(database)。
- 一个 MongoDB 实例支持多个数据库(database)。
文档(document)、集合(collection)、数据库(database)的层次结构如下图:
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB不支持 | |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
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
- 最后三个字节是随机数
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
根据你的系统下载 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 collections 或 show 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 需求分析
设计表结构:
专栏文章评论 | comment | ||
---|---|---|---|
字段名称 | 字段含义 | 字段类型 | 备注 |
_id | ID | 文本 | |
articleId | 文章ID | 文本 | |
content | 评论内容 | 文本 | |
userId | 评论人ID | 文本 | |
parentId | 评论ID | 文本 | 如果为0表示文章的顶级评论 |
publishDate | 评论日期 | 日期 | |
thumbUp | 点赞数 | 数字 |
需要实现以下功能:
- 评论基本增删改查API
- 根据文章id查询评论
- 评论点赞
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();
}