在SpringBoot项目中集成MongoDB

发布于:2024-11-23 ⋅ 阅读:(23) ⋅ 点赞:(0)

阅读本文前可以先阅读以下文章:

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());
    }

}