5.1 需求分析
这里会结合一个具体的业务场景(小案例),对用户评论进行CRUD
在这个案例中主要的需求是:
- 基本增删改查API
- 根据文章id查询评论
- 评论点赞
文章示例参考:早晨空腹喝水,是对还是错?www.toutiao.com/a6721476546…
5.2 表结构分析
数据库:articledb,集合就用我们上面一直在使用的comment
5.3 技术选型
5.3.1 mongodb-driver(了解)
mongodb-driver是mongo官方推出的java连接mongoDB的驱动包,相当于JDBC驱动。我们通过一个入门的案例来了解mongodb-driver 的基本使用。
- 官方驱动说明和下载:mongodb.github.io/mongo-java-…
- 官方驱动示例文档:mongodb.github.io/mongo-java-…
5.3.2 SpringDataMongoDB
SpringData家族成员之一,用于操作MongoDB的持久层框架,封装了底层的mongodb-driver。
官网主页: projects.spring.io/spring-data…
5.4 文章微服务模块搭建
(1)搭建项目工程article,pom.xml引入依赖:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.7.2
com.fx
article
0.0.1-SNAPSHOT
article
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-data-mongodb
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
复制代码
(2)创建application.yml
spring:
#数据源配置
data:
mongodb:
# 主机地址
host: localhost
# 数据库
database: articledb
# 默认端口是27017
port: 27017
# 也可以使用uri连接
#uri: mongodb://192.168.40.134:27017/articledb
复制代码
(3)创建启动类
com.fx.article.ArticleApplication
package com.fx.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);
}
}
复制代码
我们启动一下空项目,看Mongo
连接是否正常,一般就可以正常连接了
5.5 文章评论实体类的编写
创建实体类 创建包com.fx.article,包下建包po用于存放实体类,创建实体类
这里有一点需要注意,因为Mongo
是可以进行横向拓展的,所以可能会出现一个集合对应多个实体类的情况
package com.fx.article.po;
import lombok.Data;
import org.springframework.data.annotation.Id;
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 java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Date;
/**
*
* 文档评论实体类
* 把一个Java类生命为mongodb的文档,可以通过collection参数指定这个类对应的文档
*
*
* @since 2022-07-28 20:19
* @author 梁峰源
**/
@Data
// 若未添加@Document注解,则该bean save到mongo的comment collection
@Document(collection = "comment")//指定对应的集合,如果省略则默认为类名小写进行集合映射
@CompoundIndex(def = "{'userid': 1, 'nickname' : -1}") // 添加复合索引,先按userid排,再按nickname降序排
public class Comment implements Serializable {
private static final long serialVersionUID = 21218312631312334L;
// 主键标识,该属性的值会自动对应mongodb的主键字段`_id`, 如果该属性名叫做 `id`, 则该注解可以省略,否者必须写
@Id
private String id;//主键
private String content;//吐槽内容
private Date publishtime;//发布日期
// 添加了一个单子段的索引
@Indexed
private String userid;//发布人的ID
private String nickname;//用户昵称
private LocalDateTime createdatetime;//评论的日期时间
private Integer likenum;//点赞数
private Integer replaynum;//回复数
private String state;//状态
private String parentid;//上级ID
private String articleid;//文章id
}
复制代码
说明: 索引可以大大提升查询效率,一般在查询字段上添加索引,索引的添加可以通过Mongo的命令来添加,也可以在Java的实体类中通过注解添加。一般我们为了项目的可拓展性,会在命令行进行添加
1)单字段索引注解@Indexed
org.springframework.data.mongodb.core.index.Indexed.class
声明该字段需要索引,建索引可以大大的提高查询效率。
Mongo命令参考:
db.comment.createIndex({"userid":1})
复制代码
2)复合索引注解@CompoundIndex
org.springframework.data.mongodb.core.index.CompoundIndex.class
复合索引的声明,建复合索引可以有效地提高多字段的查询效率。
Mongo命令参考:
db.comment.createIndex({"userid":1,"nickname":-1})
复制代码
5.6 文章评论的基本增删改查
(1)创建数据访问接口 cn.itcast.article包下创建dao包,包下创建接口
com.fx.article.dao.CommentRepository
package com.fx.article.dao;
import com.fx.article.po.Comment;
import org.springframework.data.mongodb.repository.MongoRepository;
/**
*
*
*
*
* @author 梁峰源
* @since 2022-07-28 20:34
**/public interface CommentRepository extends MongoRepository {
}
复制代码
(2)创建业务逻辑类 cn.itcast.article包下创建service包,包下创建类
com.fx.article.service.CommentService
package com.fx.article.service;
import com.fx.article.dao.CommentRepository;
import com.fx.article.po.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
*
* 评论service方法
*
*
* @since 2022-07-28 20:36
* @author 梁峰源
**/
@Service
public class CommentService {
//注入dao
@Autowired
private CommentRepository commentRepository;
/**
* 保存一个评论
*/
public void saveComment(Comment comment) {
//如果需要自定义主键,可以在这里指定主键;如果不指定主键,MongoDB会自动生成主键
//设置一些默认初始值。。。
//调用dao
commentRepository.save(comment);
}
/**
* 更新评论
*/
public void updateComment(Comment comment) {
//调用dao
commentRepository.save(comment);
}
/**
* 根据id删除评论
*/
public void deleteCommentById(String id) {
//调用dao
commentRepository.deleteById(id);
}
/**
* 查询所有评论
*/
public List findCommentList() {
//调用dao
return commentRepository.findAll();
}
/**
* 根据id查询评论
*/
public Comment findCommentById(String id) {
//调用dao
return commentRepository.findById(id).get();
}
}
复制代码
(3)新建Junit测试类,测试保存和查询所有:
com.fx.itcast.article.service.CommentServiceTest
package com.fx.article.service;
import com.fx.article.po.Comment;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author Eureka
* @version 1.0
* @since 2022年7月28日20:56:33
*/@SpringBootTest
public class CommentServiceTest {
@Autowired
private CommentService commentService;
/**
* 保存一条记录
*/
@Test
public void testSaveComment() throws Exception {
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的方式和Mybatis Plus一样,直接从实体类中获取即可
Comment commentById = commentService.findCommentById(comment.getId());
System.out.println(commentById);
}
/**
* 更新一条记录
*/
@Test
public void testUpdateComment() throws Exception {
// Comment comment = new Comment();
// comment.setId("1");
// comment.setNickname("张三");
// 上面的方式会将其他字段变为空,所以可以先查询出来再更新对应字段
Comment comment = commentService.findCommentById("1");
comment.setNickname("张三");
// 更新这条记录
commentService.updateComment(comment);
// 打印一下这条记录
Comment commentById = commentService.findCommentById(comment.getId());
System.out.println(commentById);
}
/**
* Method: deleteCommentById(String id) */ @Test
public void testDeleteCommentById() throws Exception {
commentService.deleteCommentById("1");
}
/**
* Method: findCommentList() */ @Test
public void testFindCommentList() throws Exception {
List commentList = commentService.findCommentList();
System.out.println(commentList);
}
/**
* 通过id查询一条记录
*/
@Test
public void testFindCommentById() throws Exception {
Comment commentById = commentService.findCommentById("1");
System.out.println(commentById);
}
}
复制代码
这里需要注意如果是MongoDB 6
以上的版本可能打印不出来,这里可能有像MySQL中MVCC之类的同学,我换成4版本后就可以正常打印出来了
5.7 根据上级ID查询文章评论的分页列表
(1)CommentRepository新增方法定义
/**
* 分页查询,这里的名字要根据提示来,不能写错不然会生成失败
*/
Page findByUserid(String userid, Pageable pageable);
复制代码
(2)CommentService新增方法
/**
* 根据父id查询分页列表
* @param userid
* @param page 页码
* @param size 页数
*/
public Page findCommentListPageByUserid(String userid,int page ,int size){
return commentRepository.findByUserid(userid, PageRequest.of(page-1,size));
}
复制代码
测试
@Test
void testFindCommentListPageByParentid() {
Page pages = commentService.findCommentListPageByUserid("1003", 1, 3);
// 打印有多少条记录
System.out.println(pages.getTotalElements());
List contentList = pages.getContent();
// 将所有的记录都打印出来
contentList.forEach(System.out::println);
}
复制代码
5.8 MongoTemplate实现评论点赞
我们看一下以下点赞的临时示例代码: CommentService 新增updateThumbup方法
/**
* 点赞-效率低
* @param id
*/
public void updateCommentThumbupToIncrementingOld(String id){
Comment comment = commentRepository.findById(id).get();
comment.setLikenum(comment.getLikenum()+1);
commentRepository.save(comment);
}
复制代码
以上方法虽然实现起来比较简单,但是执行效率并不高,因为我只需要将点赞数加1就可以了,没必要查询出所有字段修改后再更新所有字 段。(蝴蝶效应)
我们可以使用MongoTemplate类来实现对某列的操作。
(1)修改CommentService
//注入MongoTemplate
@Autowired
private MongoTemplate mongoTemplate;
/**
* 点赞数+1
* @param id
*/
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");
}
复制代码
(2)测试用例:
@Test
void testupdateCommentLikenum() {
// 更新之前
System.out.println(commentService.findCommentById("2"));
commentService.updateCommentLikenum("2");
// 更新之后
System.out.println(commentService.findCommentById("2"));
}
复制代码
测试结果,可以看到数据已经增长了
更多的命令可以自行进行查看,这里贴一下API的地址:
- 菜鸟教程
- Spring-data MongoDB官方文档