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引入依赖:

"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 https://maven.apache.org/xsd/maven-4.0.0.xsd">  
    <modelVersion>4.0.0modelVersion>  
    <parent>  
        <groupId>org.springframework.bootgroupId>  
        <artifactId>spring-boot-starter-parentartifactId>  
        <version>2.7.2version>  
        <relativePath/>   
    parent>  
    <groupId>com.fxgroupId>  
    <artifactId>articleartifactId>  
    <version>0.0.1-SNAPSHOTversion>  
    <name>articlename>  
    <description>Demo project for Spring Bootdescription>  
    <properties>  
        <java.version>1.8java.version>  
    properties>  
    <dependencies>  
        <dependency>  
            <groupId>org.springframework.bootgroupId>  
            <artifactId>spring-boot-starter-data-mongodbartifactId>  
        dependency>  
        <dependency>  
            <groupId>org.projectlombokgroupId>  
            <artifactId>lombokartifactId>  
            <optional>trueoptional>  
        dependency>  
        <dependency>  
            <groupId>org.springframework.bootgroupId>  
            <artifactId>spring-boot-starter-testartifactId>  
            <scope>testscope>  
        dependency>  
    dependencies>  

    <build>  
        <plugins>  
            <plugin>  
                <groupId>org.springframework.bootgroupId>  
                <artifactId>spring-boot-maven-pluginartifactId>  
                <configuration>  
                    <excludes>  
                        <exclude>  
                            <groupId>org.projectlombokgroupId>  
                            <artifactId>lombokartifactId>  
                        exclude>  
                    excludes>  
                configuration>  
            plugin>  
        plugins>  
    build>  

project>
复制代码

(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命令参考:

JavaScript copyable" lang="javascript">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官方文档

作者:是小梁同学呀
链接:https://juejin.cn/post/7126746699527094303
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。