阅读本文前可以先阅读以下文章:
- MongoDB快速入门(MongoDB简介、MongoDB的应用场景、MongoDB中的基本概念、MongoDB的数据类型、MongoDB的安装与部署、MongoDB的常用命令)
- MongoDB的常用命令(数据库操作、集合操作、文档操作)
1. 准备工作
假设我们在做一个与自媒体相关的项目,项目引入了 MongoDB 存储与文章的评论数据
数据库名称为 article
,集合名称为 comment
,以下是可能用到的字段
字段名称 | 字段含义 | 字段类型 | 备注 |
---|---|---|---|
_id | ID | ObjectId或String | MongoDB文档的唯一标识符,作为主键使用 |
article_id | 文章ID | String | 文章的唯一标识符,用于关联评论和文章 |
content | 评论内容 | String | 用户发表的评论文本内容 |
user_id | 评论人ID | String | 发表评论的用户唯一标识符 |
nickname | 评论人昵称 | String | 发表评论的用户昵称,用于显示在评论列表中 |
create_time | 评论的日期时间 | Date | 评论创建的时间,格式通常为ISO日期时间格式 |
like_number | 点赞数 | Int32 | 评论获得的点赞数量,反映评论的受欢迎程度 |
reply_number | 回复数 | Int32 | 评论下方的回复数量,反映评论的互动程度 |
state | 状态 | String | 评论的可见状态,'0’表示评论不可见,'1’表示评论可见 |
parent_id | 上级ID | String | 评论的上级评论ID,如果为’0’或空,则表示该评论是顶级评论,没有上级评论 |
先创建一个名为 article 的数据库
use comment
接着运行以下 SQL,插入测试数据
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MongoDB
Source Server Version : 80003 (8.0.3)
Source Host : localhost:27017
Source Schema : test
Target Server Type : MongoDB
Target Server Version : 80003 (8.0.3)
File Encoding : 65001
*/
// ----------------------------
// Collection structure for comment
// ----------------------------
db.getCollection("comment").drop();
db.createCollection("comment");
db.getCollection("comment").createIndex({
user_id: NumberInt("1")
}, {
name: "user_id_1"
});
// ----------------------------
// Documents of comment
// ----------------------------
db.getCollection("comment").insertOne({
_id: ObjectId("6728a523d9496fae23c4c2a9"),
article_id: NumberInt("100000"),
content: "今天天气真好,阳光明媚",
user_id: "1001",
nickname: "Rose",
create_time: ISODate("2024-11-04T10:42:43.056Z"),
like_number: NumberInt("10"),
state: null
});
db.getCollection("comment").insertOne({
_id: "2",
article_id: "100001",
content: "我夏天空腹喝凉开水,冬天喝温开水",
user_id: "1005",
nickname: "伊人憔悴",
create_time: ISODate("2019-08-05T23:58:51.485Z"),
like_number: NumberInt("2422"),
state: "1"
});
db.getCollection("comment").insertOne({
_id: "3",
article_id: "100001",
content: "我一直喝凉开水,冬天夏天都喝。",
user_id: "1004",
nickname: "杰克船长",
create_time: ISODate("2019-08-06T01:05:06.321Z"),
like_number: NumberInt("667"),
state: "1"
});
db.getCollection("comment").insertOne({
_id: "4",
article_id: "100001",
content: "专家说不能空腹吃饭,影响健康。",
user_id: "1003",
nickname: "凯撒大帝",
create_time: ISODate("2019-08-06T08:18:35.288Z"),
like_number: NumberInt("2000"),
state: "1"
});
db.getCollection("comment").insertOne({
_id: "5",
article_id: "100001",
content: "研究表明,刚烧开的水千万不能喝,因为烫嘴。",
user_id: "1003",
nickname: "凯撒大帝",
create_time: ISODate("2019-08-06T11:01:02.521Z"),
like_number: NumberInt("3000"),
state: "1"
});
2. 在SpringBoot项目中集成MongoDB
本次演示使用的环境为:JDK 17.0.7 + SpringBoot 3.0.2
2.1 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2.2 编写配置文件
application.yml
server:
port: 11006
spring:
data:
mongodb:
database: article
host: 127.0.0.1
port: 27017
# username:
# password:
2.3 实体类
- 在Spring Data MongoDB中,
@Field
注解用于指定文档中字段的名称,这在字段名称与MongoDB集合中存储的字段名称不一致时非常有用- 如果实体类的字段名称与MongoDB文档中的字段名称相同,那么不需要使用
@Field
注解- 通过
@Document
注解指定集合名称
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.util.Date;
/**
* Comment 实体类,用于映射MongoDB中的 comment 集合。
*/
@Document(collection = "comment")
public class Comment {
/**
* MongoDB文档的唯一标识符,作为主键使用。
*/
@Id
private String id;
/**
* 文章的唯一标识符,用于关联评论和文章。
*/
@Field("article_id")
private String articleId;
/**
* 用户发表的评论文本内容。
*/
private String content;
/**
* 发表评论的用户唯一标识符。
*/
@Field("user_id")
private String userId;
/**
* 发表评论的用户昵称,用于显示在评论列表中。
*/
private String nickname;
/**
* 评论创建的时间,格式通常为ISO日期时间格式。
*/
@Field("create_time")
private Date createTime;
/**
* 评论获得的点赞数量,反映评论的受欢迎程度。
*/
@Field("like_number")
private Integer likeNumber;
/**
* 评论下方的回复数量,反映评论的互动程度。
*/
@Field("reply_number")
private Integer replyNumber;
/**
* 评论的可见状态,'0’表示评论不可见,'1’表示评论可见。
*/
private String state;
/**
* 评论的上级评论ID,如果为’0’或空,则表示该评论是顶级评论,没有上级评论。
*/
@Field("parent_id")
private String parentId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getArticleId() {
return articleId;
}
public void setArticleId(String articleId) {
this.articleId = articleId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
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 Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Integer getLikeNumber() {
return likeNumber;
}
public void setLikeNumber(Integer likeNumber) {
this.likeNumber = likeNumber;
}
public Integer getReplyNumber() {
return replyNumber;
}
public void setReplyNumber(Integer replyNumber) {
this.replyNumber = replyNumber;
}
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;
}
@Override
public String toString() {
return "Comment{" +
"id='" + id + '\'' +
", articleId='" + articleId + '\'' +
", content='" + content + '\'' +
", userId='" + userId + '\'' +
", nickname='" + nickname + '\'' +
", createTime=" + createTime +
", likeNumber=" + likeNumber +
", replyNumber=" + replyNumber +
", state='" + state + '\'' +
", parentId='" + parentId + '\'' +
'}';
}
}
3. 测试
- 在 SpringBoot 中操作 MongoDB,可以使用
MongoRepository
对象,也可以使用MongoTemplate
对象MongoRepository
是基于 Spring Data 的 Repository 抽象,它提供了一套标准的数据访问方法,使得 CRUD 操作变得非常简单- 如果需要进行复杂的查询或需要细粒度的控制,
MongoTemplate
可能是更好的选择
我们编写一个测试类,在测试类中注入 MongoTemplate
对象(与使用 RedisTemplate
类似)
import com.mongodb.client.MongoDatabase;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
@SpringBootTest
class MongodbApplicationTests {
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void showCurrentDatabase() {
MongoDatabase db = mongoTemplate.getDb();
System.out.println("db.getName() = " + db.getName());
}
@Test
public void showAllCollections() {
mongoTemplate.getCollectionNames().forEach(System.out::println);
}
}
4. 文档操作
4.1 插入操作
4.1.1 单次插入
@Test
public void testInsert() {
Comment comment = new Comment();
comment.setArticleId("article123");
comment.setContent("这是一个很好的文章!");
comment.setUserId("1003");
comment.setNickname("评论者A");
comment.setCreateTime(new Date());
comment.setLikeNumber(10);
comment.setReplyNumber(2);
comment.setState("1");
comment.setParentId("0");
mongoTemplate.insert(comment);
// 拿到插入后的评论ID
System.out.println("tempComment.getId() = " + comment.getId());
}
4.1.2 批量插入
@Test
public void testInsertAll() {
List<Comment> commentList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Comment comment = new Comment();
comment.setArticleId("article" + (i + 1));
comment.setContent("这是第 " + (i + 1) + " 条评论内容");
comment.setUserId(String.valueOf(1003));
comment.setNickname("评论者" + (i + 1));
comment.setCreateTime(new Date());
comment.setLikeNumber(5 + i); // 假设点赞数从5开始递增
comment.setReplyNumber(0); // 假设初始回复数为0
comment.setState("1"); // 假设所有评论都是可见的
comment.setParentId("0"); // 假设所有评论都是顶级评论
commentList.add(comment);
}
// 批量插入评论
mongoTemplate.insertAll(commentList);
}
4.2 查询操作
分页参数和跳过多少条文档需要通过
Query
对象指定
4.2.1 根据id查询
@Test
public void testFindById() {
Comment comment = mongoTemplate.findById("2", Comment.class);
System.out.println("comment = " + comment);
}
4.2.2 根据特定条件查询
@Test
public void testFindByUserIdAndCreateTime() {
String userId = "1003";
Query query = new Query();
Criteria criteria = Criteria.where("user_id").is(userId).and("create_time").lte(new Date());
query.addCriteria(criteria)
.skip(0)
.limit(5);
List<Comment> commentList = mongoTemplate.find(query, Comment.class);
commentList.forEach(System.out::println);
}
4.2.3 正则查询
@Test
public void testFindByRegex() {
String keyword = "开水";
Query query = new Query();
Criteria criteria = Criteria.where("content").regex(keyword);
query.addCriteria(criteria);
List<Comment> commentList = mongoTemplate.find(query, Comment.class);
commentList.forEach(System.out::println);
}
4.2.4 查询所有文档
@Test
public void testFindALl() {
List<Comment> commentList = mongoTemplate.findAll(Comment.class);
commentList.forEach(System.out::println);
}
4.2.5 排序后返回
@Test
public void testFindByArticleIdWithCreatedTimeAsc() {
String articleId = "100001";
Criteria criteria = Criteria.where("article_id").is(articleId)
.and("create_time").lte(new Date());
Query query = new Query();
query.addCriteria(criteria)
.with(Sort.by(Sort.Order.asc("create_time")));
List<Comment> commentList = mongoTemplate.find(query, Comment.class);
commentList.forEach(System.out::println);
}
4.3 删除操作
4.3.1 根据id删除
@Test
public void testDeleteById() {
Criteria criteria = Criteria.where("_id").is("2");
DeleteResult deleteResult = mongoTemplate.remove(new Query(criteria), Comment.class);
System.out.println("deleteResult.getDeletedCount() = " + deleteResult.getDeletedCount());
}
4.3.2 根据特定条件删除
@Test
public void testDeleteByUserIdAndCreateTime() {
String userId = "1003";
Date date = new Date();
// 删除两分钟以内发布的评论
date.setTime(date.getTime() - 2 * 60 * 1000);
Query query = new Query();
Criteria criteria = Criteria.where("user_id").is(userId)
.and("create_time").gte(date).lte(new Date())
.and("state").is("1");
query.addCriteria(criteria);
DeleteResult deleteResult = mongoTemplate.remove(query, Comment.class);
System.out.println("deleteResult.getDeletedCount() = " + deleteResult.getDeletedCount());
}
4.4 修改操作
4.4.1 修改符合条件的第一条文档(updateFirst)
@Test
public void testUpdateFirst() {
Comment comment = new Comment();
comment.setId("6728a523d9496fae23c4c2a9");
comment.setLikeNumber(102);
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(comment.getId()));
Update update = new Update();
update.set("like_number", comment.getLikeNumber());
UpdateResult updateResult = mongoTemplate.updateFirst(query, update, Comment.class);
System.out.println("updateResult.getMatchedCount() = " + updateResult.getMatchedCount());
System.out.println("updateResult.getModifiedCount() = " + updateResult.getModifiedCount());
}
4.4.2 修改符合条件的所有文档(updateMulti)
@Test
public void testUpdateMulti() {
Query query = new Query();
Criteria criteria = Criteria.where("article_id").is("100001").and("state").is("1");
query.addCriteria(criteria);
Update update = new Update();
update.set("nickname", "聂可以");
UpdateResult updateResult = mongoTemplate.updateMulti(query, update, Comment.class);
System.out.println("updateResult.getMatchedCount() = " + updateResult.getMatchedCount());
System.out.println("updateResult.getModifiedCount() = " + updateResult.getModifiedCount());
}
5. 完整的测试代码
import cn.edu.scau.pojo.Comment;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Sort;
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 java.util.ArrayList;
import java.util.Date;
import java.util.List;
@SpringBootTest
class MongodbApplicationTests {
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void showCurrentDatabase() {
MongoDatabase db = mongoTemplate.getDb();
System.out.println("db.getName() = " + db.getName());
}
@Test
public void showAllCollections() {
mongoTemplate.getCollectionNames().forEach(System.out::println);
}
@Test
public void testInsert() {
Comment comment = new Comment();
comment.setArticleId("article123");
comment.setContent("这是一个很好的文章!");
comment.setUserId("1003");
comment.setNickname("评论者A");
comment.setCreateTime(new Date());
comment.setLikeNumber(10);
comment.setReplyNumber(2);
comment.setState("1");
comment.setParentId("0");
mongoTemplate.insert(comment);
// 拿到插入后的评论ID
System.out.println("tempComment.getId() = " + comment.getId());
}
@Test
public void testInsertAll() {
List<Comment> commentList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Comment comment = new Comment();
comment.setArticleId("article" + (i + 1));
comment.setContent("这是第 " + (i + 1) + " 条评论内容");
comment.setUserId(String.valueOf(1003));
comment.setNickname("评论者" + (i + 1));
comment.setCreateTime(new Date());
comment.setLikeNumber(5 + i); // 假设点赞数从5开始递增
comment.setReplyNumber(0); // 假设初始回复数为0
comment.setState("1"); // 假设所有评论都是可见的
comment.setParentId("0"); // 假设所有评论都是顶级评论
commentList.add(comment);
}
// 批量插入评论
mongoTemplate.insertAll(commentList);
}
@Test
public void testFindById() {
Comment comment = mongoTemplate.findById("2", Comment.class);
System.out.println("comment = " + comment);
}
@Test
public void testFindALl() {
List<Comment> commentList = mongoTemplate.findAll(Comment.class);
commentList.forEach(System.out::println);
}
@Test
public void testFindByRegex() {
String keyword = "开水";
Query query = new Query();
Criteria criteria = Criteria.where("content").regex(keyword);
query.addCriteria(criteria);
List<Comment> commentList = mongoTemplate.find(query, Comment.class);
commentList.forEach(System.out::println);
}
@Test
public void testFindByUserIdAndCreateTime() {
String userId = "1003";
Query query = new Query();
Criteria criteria = Criteria.where("user_id").is(userId).and("create_time").lte(new Date());
query.addCriteria(criteria)
.skip(0)
.limit(5);
List<Comment> commentList = mongoTemplate.find(query, Comment.class);
commentList.forEach(System.out::println);
}
@Test
public void testFindByArticleIdWithCreatedTimeAsc() {
String articleId = "100001";
Criteria criteria = Criteria.where("article_id").is(articleId)
.and("create_time").lte(new Date());
Query query = new Query();
query.addCriteria(criteria)
.with(Sort.by(Sort.Order.asc("create_time")));
List<Comment> commentList = mongoTemplate.find(query, Comment.class);
commentList.forEach(System.out::println);
}
@Test
public void testDeleteById() {
Criteria criteria = Criteria.where("_id").is("672a88601f90870e2451905a");
DeleteResult deleteResult = mongoTemplate.remove(new Query(criteria), Comment.class);
System.out.println("deleteResult.getDeletedCount() = " + deleteResult.getDeletedCount());
}
@Test
public void testDeleteByUserIdAndCreateTime() {
String userId = "1003";
Date date = new Date();
// 删除两分钟以内发布的评论
date.setTime(date.getTime() - 2 * 60 * 1000);
Query query = new Query();
Criteria criteria = Criteria.where("user_id").is(userId)
.and("create_time").gte(date).lte(new Date())
.and("state").is("1");
query.addCriteria(criteria);
DeleteResult deleteResult = mongoTemplate.remove(query, Comment.class);
System.out.println("deleteResult.getDeletedCount() = " + deleteResult.getDeletedCount());
}
@Test
public void testUpdateFirst() {
Comment comment = new Comment();
comment.setId("6728a523d9496fae23c4c2a9");
comment.setLikeNumber(102);
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(comment.getId()));
Update update = new Update();
update.set("like_number", comment.getLikeNumber());
UpdateResult updateResult = mongoTemplate.updateFirst(query, update, Comment.class);
System.out.println("updateResult.getMatchedCount() = " + updateResult.getMatchedCount());
System.out.println("updateResult.getModifiedCount() = " + updateResult.getModifiedCount());
}
@Test
public void testUpdateMulti() {
Query query = new Query();
Criteria criteria = Criteria.where("article_id").is("100001").and("state").is("1");
query.addCriteria(criteria);
Update update = new Update();
update.set("nickname", "聂可以");
UpdateResult updateResult = mongoTemplate.updateMulti(query, update, Comment.class);
System.out.println("updateResult.getMatchedCount() = " + updateResult.getMatchedCount());
System.out.println("updateResult.getModifiedCount() = " + updateResult.getModifiedCount());
}
}