# mongodb_基础到进阶 -- MongoDB 高级--MongoDB 集群部署与安全性(二)
mongodb_基础到进阶 – MongoDB 高级–MongoDB 集群部署与安全性(二)
一、mongodb 仲裁节点数据操作
1、mongodb 仲裁者节点,不存放任何业务数据的,可以登录查看
另打开一个终端,登录从节点 (端口 27019),读取数据:
# 打开另一个终端
# 切换目录
cd /usr/local/mongodb/
# 登录从节点 (端口 27019)
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27019
# 查询数据库
myrs:ARBITER> show dbs
uncaught exception: Error: listDatabases failed:{
"topologyVersion" : {
"processId" : ObjectId("66aa1787c5485192848dde80"),
"counter" : NumberLong(1)
},
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotPrimaryNoSecondaryOk"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:145:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:97:12
shellHelper.show@src/mongo/shell/utils.js:960:13
shellHelper@src/mongo/shell/utils.js:842:15
@(shellhelp2):1:1
myrs:ARBITER>
# 设置 (端口 27018)为奴隶节点,允许在从成员上运行读的操作
myrs:ARBITER> rs.slaveOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.
# 或者
myrs:ARBITER> rs.slaveOk(true)
# rs.slaveOk() 该命令是 db.getMongo().setSlaveOk() 的简化命令。
# 再次查询数据库,
myrs:ARBITER> show databases
uncaught exception: Error: listDatabases failed:{
"topologyVersion" : {
"processId" : ObjectId("66aa1787c5485192848dde80"),
"counter" : NumberLong(1)
},
"ok" : 0,
"errmsg" : "node is not in primary or recovering state",
"code" : 13436,
"codeName" : "NotPrimaryOrSecondary"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:145:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:97:12
shellHelper.show@src/mongo/shell/utils.js:960:13
shellHelper@src/mongo/shell/utils.js:842:15
@(shellhelp2):1:1
myrs:ARBITER>
2、mongodb 仲裁节点,只存放副本集配置等数据。
myrs:ARBITER> use local
switched to db local
myrs:ARBITER> show collections
replset.election
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.replset
system.rollback.id
system.tenantMigration.oplogView
system.views
二、mongodb 主节点的选举原则
1、MongoDB 在副本集中,会自动进行主节点的选举,主节点选举的触发条件:
1) 主节点故障
2) 主节点网络不可达(默认心跳信息为10秒)
3) 人工干预(rs.stepDown(600))
2、MongoDB 一旦触发选举,就要根据一定规则来选主节点。选举规则是根据票数来决定谁获胜:
-
票数最高,且获得了 “大多数”成员的投票支持的节点获胜。
“大多数”的定义为:假设复制集内投票成员数量为N,则大多数为 N/2 + 1。例如:3个投票成员,
则大多数的值是2。当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,
复制集将无法提供写服务,处于只读状态。 -
若票数相同,且都获得了 “大多数”成员的投票支持的,数据新的节点获胜。
数据的新旧是通过操作日志oplog来对比的。
3、在获得票数的时候,优先级(priority)参数影响重大。
可以通过设置优先级(priority)来设置额外票数。优先级即权重,取值为0-1000,相当于可额外增加
0-1000的票数,优先级的值越大,就越可能获得多数成员的投票(votes)数。指定较高的值可使成员
更有资格成为主要成员,更低的值可使成员更不符合条件。
4、通过 rs.conf() 可以查看优先级参数。
myrs:PRIMARY> rs.conf()
{
"_id" : "myrs",
"version" : 4,
"term" : 1,
"members" : [
{
"_id" : 0,
"host" : "172.18.30.110:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"secondaryDelaySecs" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "172.18.30.110:27018",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"secondaryDelaySecs" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "172.18.30.110:27019",
"arbiterOnly" : true,
"buildIndexes" : true,
"hidden" : false,
"priority" : 0,
"tags" : {
},
"secondaryDelaySecs" : NumberLong(0),
"votes" : 1
}
],
"protocolVersion" : NumberLong(1),
"writeConcernMajorityJournalDefault" : true,
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"catchUpTimeoutMillis" : -1,
"catchUpTakeoverDelayMillis" : 30000,
"getLastErrorModes" : {
},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
},
"replicaSetId" : ObjectId("66a9e8a9ccbb5feee63a136c")
}
}
myrs:PRIMARY>
可以看出,主节点和副本节点的优先级各为 1,即,默认可以认为都已经有了一票。但选举节点,优先
级是0,(要注意是,官方说了,选举节点的优先级必须是0,不能是别的值。即不具备选举权,但具有
投票权)。
5、修改优先级
比如,下面提升从节点的优先级:
1)先将配置导入cfg变量
myrs:SECONDARY> cfg=rs.conf()
2 )然后修改值(ID号默认从0开始):
myrs:SECONDARY> cfg.members[1].priority=2
2
3 )重新加载配置
myrs:SECONDARY> rs.reconfig(cfg)
{ "ok" : 1 }
稍等片刻会重新开始选举。
三、mongodb 故障测试_1
1、副本节点故障测试:另打开一个终端,关闭27018副本节点:
1)关闭 27018 副本节点
# 打开另一个终端(172.18.30.110-测试)
# 切换目录
cd /usr/local/mongodb/
# 查看进程
root@WIN-20240529BJA:/usr/local/mongodb# ps -ef | grep mongod
root 1549 1 0 14:49 ? 00:02:32 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27017/mongod.conf
root 1604 1 0 14:56 ? 00:03:14 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
root 1919 1 0 18:52 ? 00:00:53 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27019/mongod.conf
root 2081 1337 0 19:49 pts/2 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017
root 2100 2067 0 20:04 pts/0 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27018
root 2226 2216 0 21:41 pts/4 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27019
root 2304 2289 0 22:24 pts/5 00:00:00 grep --color=auto mongod
关闭27018副本节点
root@WIN-20240529BJA:/usr/local/mongodb# kill -2 1604
2)发现,主节点和仲裁节点对27018的心跳失败。因为主节点还在,因此,没有触发投票选举。
3)此时,在主节点写入数据。
# 打开另一个终端(172.18.30.110-primary :myrs:PRIMARY> )
# 切换目录
cd /usr/local/mongodb/
# 登录主节点 (端口 27017)
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017
myrs:PRIMARY> use articledb
switched to db articledb
myrs:PRIMARY> show collections
comment
myrs:PRIMARY> db.comment.find()
{ "_id" : ObjectId("66aa2581aeb7d05de8a3a30a"), "articleid" : "100000", "content" : "今天天气真好,阳光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2024-07-31T11:52:33.679Z") }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }
# 在主节点写入数据
myrs:PRIMARY> db.comment.insert({"_id":"2","articleid":"100002","content":"李连杰动作好帅,李小龙动作好快","userid":"1003","nickname":"张三丰统一江湖","createdatetime":new Date("2024-07-31"),"likenum":NumberInt(800),"state":"1"})
# 再次查看数据
myrs:PRIMARY> db.comment.find()
{ "_id" : ObjectId("66aa2581aeb7d05de8a3a30a"), "articleid" : "100000", "content" : "今天天气真好,阳光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2024-07-31T11:52:33.679Z") }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100002", "content" : "李连杰动作好帅,李小龙动作好快", "userid" : "1003", "nickname" : "张三丰统一江湖", "createdatetime" : ISODate("2024-07-31T00:00:00Z"), "likenum" : 800, "state" : "1" }
myrs:PRIMARY>
4)再启动从节点,会发现,主节点写入的数据,会自动同步给从节点。
# 打开另一个终端(172.18.30.110-secondary :myrs:SECONDARY> )
# 切换目录
cd /usr/local/mongodb/
# 启动从节点 (端口 27018)服务
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 2350
child process started successfully, parent exiting
# 登录从节点 (端口 27018)
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27018
myrs:PRIMARY> use articledb
switched to db articledb
myrs:SECONDARY> rs.slaveOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.
myrs:SECONDARY> show collections
comment
myrs:SECONDARY> db.comment.find()
{ "_id" : ObjectId("66aa2581aeb7d05de8a3a30a"), "articleid" : "100000", "content" : "今天天气真好,阳光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2024-07-31T11:52:33.679Z") }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100002", "content" : "李连杰动作好帅,李小龙动作好快", "userid" : "1003", "nickname" : "张三丰统一江湖"

2、主节点故障测试:关闭 27017 节点
1)关闭 27017 主节点
# 打开另一个终端(172.18.30.110-测试)
# 切换目录
cd /usr/local/mongodb/
# 查看进程
root@WIN-20240529BJA:/usr/local/mongodb# ps -ef | grep mongod
root 1549 1 0 14:49 ? 00:02:54 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27017/mongod.conf
root 1919 1 0 18:52 ? 00:01:09 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27019/mongod.conf
root 2325 1337 0 22:51 pts/2 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017
root 2333 2216 0 22:59 pts/4 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27019
root 2350 1 0 23:09 ? 00:00:09 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
root 2447 2067 0 23:09 pts/0 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27018
root 2477 2289 0 23:35 pts/5 00:00:00 grep --color=auto mongod
关闭27018副本节点
root@WIN-20240529BJA:/usr/local/mongodb# kill -2 1549
2)发现,从节点和仲裁节点对27017的心跳失败,当失败超过10秒,此时因为没有主节点了,会自动发起
投票。
3)而副本节点只有27018,因此,候选人只有一个就是27018,开始投票。
27019向27018投了一票,27018本身自带一票,因此共两票,超过了“大多数”
27019是仲裁节点,没有选举权,27018不向其投票,其票数是0.
最终结果,27018成为主节点。具备读写功能。
4)在27018写入数据查看。
# 打开另一个终端(172.18.30.110-secondary :myrs:SECONDARY> )
# 切换目录
cd /usr/local/mongodb/
# 在原来从节点(端口27018)现在主节点,可以正常写入数据
myrs:SECONDARY> db.comment.insert({"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水,冬天喝温开水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"})
5)再启动 27017节点,发现27017变成了从节点,27018仍保持主节点。
# 打开另一个终端(172.18.30.110-测试)
# 切换目录
cd /usr/local/mongodb/
# 启动从节点 (端口 27018)服务
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27017/mongod.conf
6)登录27017节点,发现是从节点了,数据自动从27018同步。从而实现了高可用。
# 打开另一个终端(172.18.30.110-primary)
# 切换目录
cd /usr/local/mongodb/
# 连接 27017 节点
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017
myrs:PRIMARY>
myrs:PRIMARY>
myrs:SECONDARY>
myrs:SECONDARY>
myrs:SECONDARY> db.comment.find()
Error: error: {
"topologyVersion" : {
"processId" : ObjectId("66aa5b34fb0cb3ff1ec01d75"),
"counter" : NumberLong(3)
},
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotPrimaryNoSecondaryOk",
"$clusterTime" : {
"clusterTime" : Timestamp(1722440883, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1722440883, 1)
}
myrs:SECONDARY> rs.slaveOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.
# 获取数据 db.comment.find()
myrs:SECONDARY> db.comment.find()
{ "_id" : ObjectId("66aa2581aeb7d05de8a3a30a"), "articleid" : "100000", "content" : "今天天气真好,阳光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2024-07-31T11:52:33.679Z") }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100002", "content" : "李连杰动作好帅,李小龙动作好快", "userid" : "1003", "nickname" : "张三丰统一江湖", "createdatetime" : ISODate("2024-07-31T00:00:00Z"), "likenum" : 800, "state" : "1" }
myrs:SECONDARY>

四、mongodb 故障测试2
1、仲裁节点和主节点故障
1)先关掉仲裁节点27019,
2)关掉现在的主节点27018
3)登录27017后,发现,27017仍然是从节点,副本集中没有主节点了,导致此时,副本集是只读状态,
无法写入。
4)为啥不选举了?因为27017的票数,没有获得大多数,即没有大于等于2,它只有默认的一票(优先级
是1)
5)如果要触发选举,随便加入一个成员即可。
- 如果只加入 27019仲裁节点成员,则主节点一定是27017,因为没得选了,仲裁节点不参与选举,
但参与投票。 - 如果只加入 27018节点,会发起选举。因为27017和27018都是两票,则按照谁数据新,谁当主节
点。
2、仲裁节点和从节点故障
1)先关掉仲裁节点27019,
2)关掉现在的副本节点27018
3)10秒后,27017主节点自动降级为副本节点。(服务降级)
4)副本集不可写数据了,已经故障了。

五、mongodb Compass & SpringData 连接副本集
1、先启动3个服务
# 切换目录
cd /usr/local/mongodb/
# 启动 mongodb 服务
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27019/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 2904
child process started successfully, parent exiting
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 2978
child process started successfully, parent exiting
# 查看进程
root@WIN-20240529BJA:/usr/local/mongodb# ps -ef | grep mongod
root 2325 1337 0 Jul31 pts/2 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017
root 2333 2216 0 Jul31 pts/4 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27019
root 2447 2067 0 Jul31 pts/0 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27018
root 2559 1 0 Jul31 ? 00:00:18 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27017/mongod.conf
root 2904 1 3 00:26 ? 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27019/mongod.conf
root 2978 1 11 00:26 ? 00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
root 3066 2289 0 00:26 pts/5 00:00:00 grep --color=auto mongod
2、如果使用云服务器需要修改配置中的主节点ip
var config = rs.config();
config.members[0].host="180.76.159.126:27017";rs.reconfig(config)
3、compass 连接:
27-compass连接-1.png
27-compass连接-2.png
说明:
虽然用 27018 端口,连接的从节点,但也可以进行插入数据的写操作,
因为用 compass 连接的是整个副本集。它会自动切换到 主节点,进行写操作。
4、SpringDataMongoDB 连接副本集
1)副本集语法:
mongodb://host1,host2,host3/articledb?
connect=replicaSet&slaveOk=true&replicaSet=副本集名字
2)其中:
slaveOk=true :开启副本节点读的功能,可实现读写分离。
connect=replicaSet :自动到副本集中选择读写的主机。如果slaveOK是打开的,则实现了读写分
离
3)示例:
连接 replica set 三台服务器 (端口 27017, 27018, 和27019),直接连接第一个服务器,无论是replica
set一部分或者主服务器或者从服务器,写入操作应用在主服务器 并且分布查询到从服务器。
spring:
#数据源配置
data:
mongodb:
# 主机地址
# host: 172.18.30.110
# 数据库
# database: articledb
# 默认端口是27017
# port: 27017
#也可以使用uri连接
#uri: mongodb://192.168.40.134:27017/articledb
# 副本集的连接字符串
uri:
mongodb://172.18.30.110:27017,172.18.30.110:27018,172.18.30.110:27019/article
db?connect=replicaSet&slaveOk=true&replicaSet=myrs
4)注意:
- 主机必须是副本集中所有的主机,包括主节点、副本节点、仲裁节点。
- SpringDataMongoDB自动实现了读写分离:
- 写操作时,只打开主节点连接:
- 读操作是,同时打开主节点和从节点连接,但使用从节点获取数据:
5、打开 idea,创建 article-mongodb 的 maven 工程。
--> idea --> File
--> New --> Project
--> Maven
Project SDK: ( 1.8(java version "1.8.0_131" )
--> Next
--> Groupld : ( djh.it )
Artifactld : ( article-mongodb )
Version : 1.0-SNAPSHOT
--> Name: ( article-mongodb )
Location: ( ...\article-mongodb\ )
--> Finish
6、在工程 article-mongodb (模块)中的 pom.xml 中导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>djh.it</groupId>
<artifactId>article-mongodb</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath></relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
</project>
<!-- article-mongodb\pom.xml -->
7、在工程 article-mongodb (模块)中,创建配置文件 application.yml
# article-mongodb\src\main\resources\application.yml
spring:
# 数据源配置
data:
mongodb:
# # 主机地址
# host: 172.18.30.110
# # 数据库
# database: articledb
# # 默认端口27017
# port: 27017
# # 也可以使用uri连接
# # uri mongodb //172.18.30.110:27017/articledb
uri: mongodb://172.18.30.110:27017,172.18.30.110:27018,172.18.30.110:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=myrs
8、在工程 article-mongodb (模块)中,创建启动类 ArticleApplication.java
/**
* article-mongodb\src\main\java\djh\it\article\ArticleApplication.java
*
* 2024-7-28 创建启动类 ArticleApplication.java
*/
package djh.it.article;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ArticleApplication {
public static void main( String[] args ) {
SpringApplication.run(ArticleApplication.class, args);
}
}
9、在工程 article-mongodb (模块)中,创建 文章评论实体类 Comment.java
/**
* article-mongodb\src\main\java\djh\it\article\pojo\Comment.java
*
* 2024-7-28 创建 文章评论实体类 Comment.java
*/
package djh.it.article.pojo;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Date;
//把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。
//@Document(collection="mongodb 对应 collection 名")
// 若未加 @Document ,该 bean save 到 mongo 的 comment collection
// 若添加 @Document ,则 save 到 comment collection
@Document(collection="comment")//可以省略,如果省略,则默认使用类名小写映射集合
//复合索引
@CompoundIndex( def = "{'userid': 1, 'nickname': -1}")
public class Comment implements Serializable {
//主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写
// @Id
private String id;//主键
//该属性对应mongodb的字段的名字,如果一致,则无需该注解
@Field("content")
private String content;//吐槽内容
private Date publishtime;//发布日期
//添加了一个单字段的索引
@Indexed
private String userid;//发布人ID
private String nickname;//昵称
private LocalDateTime createdatetime;//评论的日期时间
private Integer likenum;//点赞数
private Integer replynum;//回复数
private String state;//状态
private String parentid;//上级ID
private String articleid;
//getter and setter.....
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getPublishtime() {
return publishtime;
}
public void setPublishtime(Date publishtime) {
this.publishtime = publishtime;
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public LocalDateTime getCreatedatetime() {
return createdatetime;
}
public void setCreatedatetime(LocalDateTime createdatetime) {
this.createdatetime = createdatetime;
}
public Integer getLikenum() {
return likenum;
}
public void setLikenum(Integer likenum) {
this.likenum = likenum;
}
public Integer getReplynum() {
return replynum;
}
public void setReplynum(Integer replynum) {
this.replynum = replynum;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getParentid() {
return parentid;
}
public void setParentid(String parentid) {
this.parentid = parentid;
}
public String getArticleid() {
return articleid;
}
public void setArticleid(String articleid) {
this.articleid = articleid;
}
@Override
public String toString() {
return "Comment{" +
"id='" + id + '\'' +
", content='" + content + '\'' +
", publishtime=" + publishtime +
", userid='" + userid + '\'' +
", nickname='" + nickname + '\'' +
", createdatetime=" + createdatetime +
", likenum=" + likenum +
", replynum=" + replynum +
", state='" + state + '\'' +
", parentid='" + parentid + '\'' +
", articleid='" + articleid + '\'' +
'}';
}
}
10、在工程 article-mongodb (模块)中,创建 dao 接口类 CommentRepository.java
/**
* article-mongodb\src\main\java\djh\it\article\dao\CommentRepository.java
*
* 2024-7-28 创建 dao 接口类 CommentRepository.java
*/
package djh.it.article.dao;
import djh.it.article.pojo.Comment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface CommentRepository extends MongoRepository<Comment,String > {
//根据父id,查询子评论的分页列表, 方法名固定,不能随便更改。否则会报错
Page<Comment> findByParentid(String parentid,Pageable pageable);
}
11、在工程 article-mongodb (模块)中,创建 service 类 CommentService.java
/**
* article-mongodb\src\main\java\djh\it\article\service\CommentService.java
*
* 2024-7-28 创建 service 类 CommentService.java
*/
package djh.it.article.service;
import djh.it.article.dao.CommentRepository;
import djh.it.article.pojo.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
//注入MongoTemplate
@Autowired
private MongoTemplate mongoTemplate;
/**
* 保存一个评论
* @param comment
*/
public void saveComment( Comment comment){
//如果需要自定义主键,可以在这里指定主键;如果不指定主键,MongoDB会自动生成主键
//设置一些默认初始值。。。
//调用dao
commentRepository.save(comment);
}
/**
* 更新评论
* @param comment
*/
public void updateComment(Comment comment){
//调用dao
commentRepository.save(comment);
}
/**
* 根据id删除评论
* @param id
*/
public void deleteCommentById(String id){
//调用dao
commentRepository.deleteById(id);
}
/**
* 查询所有评论
* @return
*/
public List<Comment> findCommentList(){
//调用dao
return commentRepository.findAll();
}
/**
* 根据id查询评论
* @param id
* @return
*/
public Comment findCommentById(String id){
//调用dao
return commentRepository.findById(id).get();
}
/**
* 根据父id查询分页列表,方法名固定,不能随便更改。否则会报错
* @param parentid
* @param page
* @param size
* @return
*/
public Page<Comment> findCommentListByParentid(String parentid,int page,int size) {
return commentRepository.findByParentid(parentid,PageRequest.of(page-1,size));
}
/**
* 点赞-此方法虽然实现起来比较简单,但是执行效率并不高,因为我只需要将点赞数加1就可以了,没必要查询出所有字段修改后再更新所有字
段。(蝴蝶效应),效率低
* @param id
*/
public void updateCommentThumbupToIncrementingOld(String id) {
Comment comment = commentRepository.findById(id).get();
comment.setLikenum(comment.getLikenum() + 1);
commentRepository.save(comment);
}
//MongoTemplate实现评论点赞, 点赞数+1,执行效率较高。
public void updateCommentLikenum(String id){
// 查询条件
Query query = Query.query(Criteria.where("_id").is(id));
// 更新条件
Update update = new Update();
// //局部更新,相当于$set
// update.set(key,value)
// //递增$inc
// update.inc("likenum",1);
update.inc("likenum");
//参数1:查询对象
//参数2:更新对象
//参数3:集合的名字或实体类的类型Comment.class
mongoTemplate.updateFirst(query,update,Comment.class);
}
}
12、在工程 article-mongodb (模块)中,创建 测试类 CommentServiceTest.java 进行测试。
/**
* article-mongodb\src\test\java\djh\it\article\service\CommentServiceTest.java
*
* 2024-7-28 创建 测试类 CommentServiceTest.java
*/
package djh.it.article.service;
import djh.it.article.pojo.Comment;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.test.context.junit4.SpringRunner;
import java.time.LocalDateTime;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class CommentServiceTest {
@Autowired
private CommentService commentService;
@Test
public void testFindCommentList() {
List<Comment> commentList = commentService.findCommentList();
System.out.println(commentList);
}
@Test
public void testFindCommentById() {
Comment commentById = commentService.findCommentById("1");
System.out.println(commentById);
}
@Test
public void testSaveComment(){
Comment comment=new Comment();
comment.setArticleid("100000");
comment.setContent("测试添加的数据");
comment.setCreatedatetime(LocalDateTime.now());
comment.setUserid("1003");
comment.setNickname("凯撒大帝");
comment.setState("1");
comment.setLikenum(0);
comment.setReplynum(0);
commentService.saveComment(comment);
}
//测试根据父id查询子评论的分页列表
@Test
public void testFindCommentListByParentid() {
Page<Comment> page = commentService.findCommentListByParentid("3", 1, 2);
System.out.println("======总记录数: "+page.getTotalElements());
System.out.println("======当前页数据: "+page.getContent());
}
//点赞测试1:方法简单,执行效率低
@Test
public void testUpdateCommentThumbupToIncrementingOld(){
commentService.updateCommentThumbupToIncrementingOld("1");
}
//点赞测试2:方法稍复杂,执行效率高
@Test
public void testUpdateCommentLikenum() {
commentService.updateCommentLikenum("1");
}
}
更多推荐

所有评论(0)