一 环境搭建
1 导入依赖
生成springboot项目后倒入所需要的mongodb依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2 数据库表 – 测试数据
dahuang> db.comment.find()
[
{
_id: ObjectId('685a3f60576e6446ef6c4bd0'),
content: '齐夏这波操作简直6到飞起,不愧是「回响」界的扛把子!',
publishtime: ISODate('2025-06-24T11:27:27.000Z'),
userid: '1001',
nickname: '齐夏的小迷弟',
createdatetime: ISODate('2025-06-24T11:27:27.260Z'),
likenum: 42,
replynum: 8,
state: '1',
parentid: null,
articleid: '100000'
},
{
_id: ObjectId('685a3f60576e6446ef6c4bd1'),
content: '乔家劲大佬又双叒叕carry全场,这波「天龙」直接封神!',
publishtime: ISODate('2025-06-24T11:27:28.000Z'),
userid: '1002',
nickname: '劲哥的狗腿子',
createdatetime: ISODate('2025-06-24T11:27:28.260Z'),
likenum: 66,
replynum: 12,
state: '1',
parentid: null,
articleid: '100001'
},
{
_id: ObjectId('685a3f60576e6446ef6c4bd2'),
content: '林檎小姐姐的「回响」太绝了,直接把BOSS秀到头皮发麻!',
publishtime: ISODate('2025-06-24T11:27:29.000Z'),
userid: '1003',
nickname: '檎檎的舔狗',
createdatetime: ISODate('2025-06-24T11:27:29.260Z'),
likenum: 33,
replynum: 5,
state: '1',
parentid: null,
articleid: '100002'
},
{
_id: ObjectId('685a3f60576e6446ef6c4bd3'),
content: '陈俊南这波嘴炮输出直接拉满,BOSS都被他说得怀疑人生了!',
publishtime: ISODate('2025-06-24T11:27:30.000Z'),
userid: '1004',
nickname: '南哥的捧哏',
createdatetime: ISODate('2025-06-24T11:27:30.260Z'),
likenum: 55,
replynum: 10,
state: '1',
parentid: null,
articleid: '100003'
},
{
_id: ObjectId('685a3f60576e6446ef6c4bd4'),
content: '程雪的「回响」太治愈了,直接把队友从残血奶到满血!',
publishtime: ISODate('2025-06-24T11:27:31.000Z'),
userid: '1005',
nickname: '雪姐的迷妹',
createdatetime: ISODate('2025-06-24T11:27:31.260Z'),
likenum: 28,
replynum: 4,
state: '1',
parentid: 10086,
articleid: '100004'
}
]
3 项目截图
4 配置文件
spring:
data:
mongodb:
# 主机IP地址,此处本机
host: localhost
# 数据库名称
database: dahuang
# 端口(默认是 27017,可省略)
port: 27017
5 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
// 指定 MongoDB 集合名 若与类名相同可省略 MongoDB 的集合名是区分大小写的
@Document(collection = "comment")
// 复合索引
@CompoundIndexes({
@CompoundIndex(name = "userid_nickname_idx", def = "{'userid': 1, 'nickname': -1}")
})
public class Comment implements Serializable {
@Id // 标记为主键,对应 MongoDB 的 "_id" 字段
private String id;
@Field("content") // 指定 MongoDB 字段名
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(回复的评论 ID)
private String articleid; // 关联的文章 ID
}
6 继承 MongoDB 仓库接口
// CommentRepository 是一个 Spring Data MongoDB 仓库接口 拥有基本的 CRUD 操作
public interface CommentRepository extends MongoRepository<Comment, String> {
// Comment:实体类类型,表示该仓库操作的文档类型。
// String:主键(_id)的类型,这里是 String 类型。
}
7 Service 层 – 测试方法
@Service
public class CommentService {
private final CommentRepository commentRepository;
public CommentService(CommentRepository commentRepository) {
this.commentRepository = commentRepository;
}
// 保存评论
public void saveComment(Comment comment) {
// 设置默认值(可选)
if (comment.getLikenum() == null) {
comment.setLikenum(0);
}
if (comment.getReplynum() == null) {
comment.setReplynum(0);
}
if (comment.getState() == null) {
comment.setState("active");
}
commentRepository.save(comment);
}
// 更新评论
public void updateComment(Comment comment) {
commentRepository.save(comment);
}
// 根据 ID 删除评论
public void deleteCommentById(String id) {
commentRepository.deleteById(id);
}
// 查询所有评论
public List<Comment> findCommentList() {
return commentRepository.findAll();
}
// 根据 ID 查询评论
public Comment findCommentById(String id) {
return commentRepository.findById(id).orElseThrow(() ->
new RuntimeException("评论不存在,ID: " + id)
);
}
}
8 测试类
@SpringBootTest
public class CommentServiceTest {
@Autowired
private CommentService commentService;
// 查所有
@Test
public void testFindCommentList() {
List<Comment> commentList = commentService.findCommentList();
System.out.println(commentList);
}
// 插入一个
@Test
public void testSaveComment() {
// 创建一个评论对象
Comment comment = new Comment();
// 若不指定ID 则会自动生成
comment.setArticleid("100000"); // 关联的文章 ID
comment.setContent("齐夏这波操作简直6到飞起,不愧是「回响」界的扛把子!"); // 评论内容
comment.setCreatedatetime(LocalDateTime.now()); // 当前时间
comment.setUserid("1003"); // 用户 ID
comment.setNickname("齐夏的小迷弟"); // 用户昵称
comment.setState("1"); // 状态:1 表示正常
comment.setLikenum(42); // 点赞数(有趣的数字)
comment.setReplynum(8); // 回复数
comment.setParentid(null); // 父评论 ID(null 表示顶级评论)
// 保存评论
commentService.saveComment(comment);
System.out.println("评论保存成功!");
}
}
[Comment(id=685a3f60576e6446ef6c4bd0, content=齐夏这波操作简直6到飞起,不愧是「回响」界的扛把子!, publishtime=Tue Jun 24 19:27:27 GMT+08:00 2025, userid=1001, nickname=齐夏的小迷弟, createdatetime=2025-06-24T19:27:27.260, likenum=42, replynum=8, state=1, parentid=null, articleid=100000),
Comment(id=685a3f60576e6446ef6c4bd1, content=乔家劲大佬又双叒叕carry全场,这波「天龙」直接封神!, publishtime=Tue Jun 24 19:27:28 GMT+08:00 2025, userid=1002, nickname=劲哥的狗腿子, createdatetime=2025-06-24T19:27:28.260, likenum=66, replynum=12, state=1, parentid=null, articleid=100001),
Comment(id=685a3f60576e6446ef6c4bd2, content=林檎小姐姐的「回响」太绝了,直接把BOSS秀到头皮发麻!, publishtime=Tue Jun 24 19:27:29 GMT+08:00 2025, userid=1003, nickname=檎檎的舔狗, createdatetime=2025-06-24T19:27:29.260, likenum=33, replynum=5, state=1, parentid=null, articleid=100002),
Comment(id=685a3f60576e6446ef6c4bd3, content=陈俊南这波嘴炮输出直接拉满,BOSS都被他说得怀疑人生了!, publishtime=Tue Jun 24 19:27:30 GMT+08:00 2025, userid=1004, nickname=南哥的捧哏, createdatetime=2025-06-24T19:27:30.260, likenum=55, replynum=10, state=1, parentid=null, articleid=100003),
Comment(id=685a3f60576e6446ef6c4bd4, content=程雪的「回响」太治愈了,直接把队友从残血奶到满血!, publishtime=Tue Jun 24 19:27:31 GMT+08:00 2025, userid=1005, nickname=雪姐的迷妹, createdatetime=2025-06-24T19:27:31.260, likenum=28, replynum=4, state=1, parentid=10086, articleid=100004)]
评论保存成功!
二 评论分页列表查询
1 更新接口
// 通过上级id`parentid`进行查询分页
public interface CommentRepository extends MongoRepository<Comment, String> {
// 自定义的分页查询方法 返回 Page<Comment>,表示分页结果(包含数据、页码、总页数等信息)。
// String parentid:父评论的 ID(null 表示顶级评论)。
// Pageable pageable:分页参数(如页码、每页大小、排序等)。
// 方法名需要与实体类属性名相对应(有固定的命名格式)
Page<Comment> findByParentid(String parentid, Pageable pageable);
}
2 更新 Service 方法
@Service
public class CommentService {
public Page<Comment> findCommentListByParentid(String parentid, int page, int size) {
// page 为当前页码 size 为每页包含多少条数据
// Page从0开始计数 第1页实际上是第0页 因此需要 page - 1
return commentRepository.findByParentid(parentid, PageRequest.of(page - 1, size));
}
}
3 更新测试方法
@SpringBootTest
public class CommentServiceTest {
@Test
public void testFindCommentListByParentid() {
// 调用分页查询方法
Page<Comment> page = commentService.findCommentListByParentid("10086", 1, 3);
// 输出分页信息
System.out.println("总记录数: " + page.getTotalElements());
System.out.println("当前页数据: " + page.getContent());
}
}
4 分页查询结果
总记录数: 1
当前页数据: [Comment(id=685a3f60576e6446ef6c4bd4, content=程雪的「回响」太治愈了,直接把队友从残血奶到满血!, publishtime=Tue Jun 24 19:27:31 GMT+08:00 2025, userid=1005, nickname=雪姐的迷妹, createdatetime=2025-06-24T19:27:31.260, likenum=28, replynum=4, state=1, parentid=10086, articleid=100004)]
三 实现评论点赞
1 更新Service方法 – 注入 MongoTemplate
@Service
public class CommentService {
// Spring Data MongoDB 提供的核心类,用于直接操作 MongoDB 数据库。
// 它封装了 MongoDB 的 CRUD 操作(如查询、插入、更新、删除),并提供了丰富的 API。
@Autowired
private MongoTemplate mongoTemplate;
public void updateCommentLikenum(String id) {
// 查询条件 创建一个查询对象,条件是 _id 字段等于传入的 id (条件可替换成其他字段)
// Spring Data MongoDB 提供的查询条件构建方式,可多个条件并列使用
Query query = Query.query(Criteria.where("_id").is(id));
// 更新条件 创建一个更新操作对象 使用 $inc 操作符将 likenum 字段的值加 1。
Update update = new Update();
update.inc("likenum", 1); // 使用 $inc 操作符递增字段值
// 执行更新操作 更新匹配到的第一条记录。 query:查询条件
// update:更新操作。Comment.class:实体类类型,用于指定操作的集合(comments)
mongoTemplate.updateFirst(query, update, Comment.class);
}
}
2 更新测试方法
@Test
public void testUpdateCommentLikenum() {
String commentId = "1"; // 假设评论 ID 为 "1"
commentService.updateCommentLikenum(commentId);
}