目录

  • 前言
  • 1、表及字段设计
  • 2、引入pom
  • 3、yml配置
  • 4、实体类创建
  • 5、Repository
  • 5.1、基础实现
  • 5.2、方法查询
  • 5.3、语句查询
  • 6、具体实现
  • 0、创建Repository
  • 1、新增数据
  • 2、修改数据
  • 3、删除数据
  • 4、根据ID查询
  • 5、聚合查询
  • 6、列表分页+条件+排序查询


前言

使用SpringBoot+MongoDB整合实现文章评论的实例操作。

1、表及字段设计

数据库名称:articledb

名称

含义

类型

_id

主键

ObjectId或String

articleId

文章ID

String

content

评论内容

String

userId

评论人ID

String

nickname

评论人名称

String

createTime

创建时间

Date

likeNum

点赞数量

Int32

replyNum

回复数量

Int32

state

状态(0:不可见;1:可见;)

String

parentId

父级评论ID(0为顶级)

String

2、引入pom

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

3、yml配置

spring:
  #数据源配置
  data:
    mongodb:
      # 单机,root与123456分别为账号和密码
      uri: mongodb://127.0.0.1:27017/articledb
      # 集群连接,testSet为副本集名称,如果开启验证mongodb://user:pwd@111....
      # uri: mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=testSet

4、实体类创建

@Document(collection = "comment")
@Data
public class Comment implements Serializable {

    /**
     * 主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写
     */
    @Id
    private String id;

    /**
     * 文章ID
     */
    private String articleId;

    /**
     * 该属性对应mongodb的字段的名字,如果一致,则无需该注解
     */
    @Field("content")
    private String content;

    /**
     * 发布人ID,并且设置为单字段索引
     */
    private String userId;
    /**
     * 昵称
     */
    private String nickName;

    /**
     * 评论的日期时间
     */
    private LocalDateTime createTime;

    /**
     * 点赞数
     */
    private Integer likeNum;

    /**
     * 回复数
     */
    private Integer replyNum;

    /**
     * 状态(0:不可见;1:可见;)
     */
    private String state;

    /**
     * 上级ID
     */
    private String parentId;
}

5、Repository

5.1、基础实现

通过实现MongoRepository<T, ID>接口,创建Repository类,其中T表示实体类的class,ID表示主键的数据类型,比如:

public interface CommentRepository extends MongoRepository<Comment, String> {}

在实现MongoRepository<T, ID>接口后,会默认实现以下接口:

<S extends T> List<S> saveAll(Iterable<S> var1);

    List<T> findAll();

    List<T> findAll(Sort var1);

    <S extends T> S insert(S var1);

    <S extends T> List<S> insert(Iterable<S> var1);

    <S extends T> List<S> findAll(Example<S> var1);

    <S extends T> List<S> findAll(Example<S> var1, Sort var2);

	Optional<T> findById(ID var1);

    boolean existsById(ID var1);

    Iterable<T> findAll();

    Iterable<T> findAllById(Iterable<ID> var1);

    long count();

    void deleteById(ID var1);

    void delete(T var1);

    void deleteAll(Iterable<? extends T> var1);

    void deleteAll();

5.2、方法查询

除此之外也可以根据具体的方法名称进行查询相应内容,比如:findByName(String name)findByNameAndUserId(String name,String userId)

5.3、语句查询

通过@Query编写类似sql的查询语句,支持动态传入参数,比如:

@Query(value="{\"username\":{\"$regex\":?0},\"age\":{\"$gte\":?1,\"$lte\": ?2}}")
Page<User> findByNameAndAgeRange(String name,int age,int ageTo,Pageable page);

6、具体实现

0、创建Repository

public interface CommentRepository extends MongoRepository<Comment, String> {

}

1、新增数据

@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
	 @Resource
    private CommentRepository commentRepository;

    @Resource
    private MongoTemplate mongoTemplate;
    
    @PostMapping
    public void saveComment(@RequestBody Comment comment) {
       int id = (int) (Math.random() * 10000000);
        int userId = (int) (Math.random() * 10);
        int articleId = (int) (Math.random() * 10);

        comment.setId(id + "");
        comment.setReplyNum(articleId);
        comment.setLikeNum(userId);
        comment.setArticleId(articleId + "");
        comment.setUserId(userId + "");
        comment.setNickName("测试人员");
        comment.setParentId("0");
        comment.setState("1");
        comment.setContent("测试内容");
        comment.setCreateTime(System.currentTimeMillis());
        commentRepository.save(comment);

        // 如果批量新增
        List<Comment> list = new ArrayList<>();
        for (int i = 0; i <= 2; i++) {
            Comment comment2 = new Comment();
            BeanUtils.copyProperties(comment, comment2);
            int newId = (int) (Math.random() * 100000);
            int newUserId = (int) (Math.random() * 10);
            int newArticleId = (int) (Math.random() * 10);
            comment2.setId(newId + "");
            comment2.setReplyNum(articleId);
            comment2.setLikeNum(userId);
            comment2.setArticleId(newArticleId + "");
            comment2.setUserId(newUserId + "");
            comment.setNickName("测试人员");
            comment.setParentId("0");
            comment.setState("1");
            comment.setContent("测试内容");
            comment2.setCreateTime(System.currentTimeMillis());
            list.add(comment2);
        }
        commentRepository.saveAll(list);
    }
}

2、修改数据

@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
	 @Resource
    private CommentRepository commentRepository;

    @Resource
    private MongoTemplate mongoTemplate;
    
    @PutMapping
    public void updateComment(@RequestBody Comment comment) {
        comment.setId("1847892");
        comment.setNickName("测试人员-修改");
        comment.setParentId("0");
        comment.setState("1");
        comment.setContent("测试内容");
        comment.setCreateTime(System.currentTimeMillis());
        // 使用save时,如果数据存在则更新,不存在则新增
        commentRepository.save(comment);
    }
}

3、删除数据

@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
	 @Resource
    private CommentRepository commentRepository;

    @Resource
    private MongoTemplate mongoTemplate;
    
    // 根据Id删除
    @DeleteMapping("/{id}")
    public void deleteCommentById(@PathVariable("id") String id) {
         commentRepository.deleteById(id);
    }
    
    // 按条件删除
    @DeleteMapping("/condition")
    public void deleteCommentByArticleId() {
        // 删除articleId为10的数据
        Query query = new Query(Criteria.where("articleId").is("10"));
        mongoTemplate.remove(query, Comment.class);
    }
}

4、根据ID查询

@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
	 @Resource
    private CommentRepository commentRepository;

    @Resource
    private MongoTemplate mongoTemplate;
    
    @GetMapping("/{id}")
    public Object findCommentById(@PathVariable("id") String id) {
        /**
         * mongoTemplate实现
         * Query query = new Query(Criteria.where("_id").is(id));
         * mongoTemplate.remove(query, Comment.class);
         */
        return commentRepository.findById(id);
    }
}

5、聚合查询

@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
	 @Resource
    private CommentRepository commentRepository;

    @Resource
    private MongoTemplate mongoTemplate;
    
    @GetMapping("/agg")
    public ResponseObject agg() {
        // 1、单/多字段分组 - 分页排序
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.group("userId", "articleId").count().as("userNum"),
                Aggregation.skip(1L),
                Aggregation.limit(10),
                Aggregation.sort(Sort.Direction.DESC, "userNum")
        );
        AggregationResults<BasicDBObject> basicDBObjects = mongoTemplate.aggregate(aggregation, Comment.class, BasicDBObject.class);
        System.out.println("basicDBObjects:");
        for (DBObject obj : basicDBObjects) {
            System.out.println(obj);
        }

        // 2、条件匹配分组,$match表示匹配条件,在group前表示where,在后表示having,userid为分组字段
        // select userid, count(*) as num from comment where articleId in(4,5) likeNum >= 1 and likeNum =<10  group by userid having num >=2
        Aggregation aggregation2 = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("likeNum").gte(1).lte(10).and("articleId").in("4", "5")),
                Aggregation.group("userId").count().as("userNum"),
                Aggregation.match(Criteria.where("userNum").gte(2))
        );
        AggregationResults<BasicDBObject> basicDBObjects2 = mongoTemplate.aggregate(aggregation2, Comment.class, BasicDBObject.class);
        System.out.println("basicDBObjects2:");
        for (DBObject obj : basicDBObjects2) {
            System.out.println(obj);
        }

        // 3、聚合-求某个数值平均值、最大值、最小值
        Aggregation aggregation3 = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("likeNum").gte(1).lte(10)),
                Aggregation.group("userId").sum("likeNum").as("likeNumSum")
                        .avg("likeNum").as("likeNumAvg")
                // Aggregation.match(Criteria.where("userNum").gte(2))
        );
        AggregationResults<BasicDBObject> basicDBObjects3 = mongoTemplate.aggregate(aggregation3, Comment.class, BasicDBObject.class);
        System.out.println("basicDBObjects3:");
        for (DBObject obj : basicDBObjects3) {
            System.out.println(obj);
        }

        // 4、聚合分组数组 ,等同于group_concat,push-元素可重复,addToSet-元素不可重复
        Aggregation aggregation4 = Aggregation.newAggregation(
                Aggregation.group("userId")
                        .addToSet("articleId").as("setList")
                        .push("articleId").as("pushList")
        );
        AggregationResults<BasicDBObject> basicDBObjects4 = mongoTemplate.aggregate(aggregation4, Comment.class, BasicDBObject.class);
        System.out.println("basicDBObjects4:");
        for (DBObject obj : basicDBObjects4) {
            System.out.println(obj);
        }
    }
}

6、列表分页+条件+排序查询

为了实现统一的分页查询,封装分页工具类:PageBuilderNewPageBean,代码如下:
PageBuilder:

import org.springframework.data.mongodb.core.query.Query;

public class PageBuilder {

    /**
     * @param query    查询
     * @param pageSize 当前页数
     * @param pageNum  当前页码
     */
    public static void startPage(Query query, Integer pageSize, Integer pageNum) {
        if (pageSize == null || pageNum == null) {
            pageSize = 10;
            pageNum = 1;
        }
        int offset = (pageNum - 1) * pageSize;
        query.skip(offset).limit(pageSize);
    }
}

NewPageBean:

import com.lhz.search.model.param.PageParam;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;

import java.util.List;

public class NewPageBean<T> {
    private Long total;
    private List<T> list;
    private MyPage pageInfo;


    public NewPageBean(List<T> list, Class<?> entityClass, PageParam param, MongoTemplate mongoTemplate) {
        Integer pageNum = param.getPageNum();
        Integer pageSize = param.getPageSize();

        long totalSize = mongoTemplate.count(new Query(), entityClass);

        //计算总页数
        long totalPage = totalSize % pageSize == 0 ? totalSize / pageSize : totalSize / pageSize + 1;

        // 数据组装
        this.list = list;
        this.total = totalSize;
        MyPage page = new MyPage();
        page.setTotalPage(totalPage);
        page.setPageNum(pageNum);
        page.setPageSize(pageSize);
        page.setPreviousPage(pageNum - 1);
        page.setNextPage(pageNum + 1);
        this.pageInfo = page;
    }

    public long getNum() {
        return this.total;
    }

    public void setNum(Long total) {
        this.total = total;
    }

    public Long getTotal() {
        return total;
    }

    public void setTotal(Long total) {
        this.total = total;
    }

    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }

    public MyPage getPageInfo() {
        return pageInfo;
    }

    public void setPageInfo(MyPage pageInfo) {
        this.pageInfo = pageInfo;
    }

    class MyPage {
        private int pageNum;
        private int pageSize;
        private long totalPage;
        private int nextPage;
        private int previousPage;

        public int getPageNum() {
            return pageNum;
        }

        public void setPageNum(int pageNum) {
            this.pageNum = pageNum;
        }

        public int getPageSize() {
            return pageSize;
        }

        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }

        public long getTotalPage() {
            return totalPage;
        }

        public void setTotalPage(long totalPage) {
            this.totalPage = totalPage;
        }

        public int getNextPage() {
            return nextPage;
        }

        public void setNextPage(int nextPage) {
            this.nextPage = nextPage;
        }

        public int getPreviousPage() {
            return previousPage;
        }

        public void setPreviousPage(int previousPage) {
            this.previousPage = previousPage;
        }
    }
}

查询参数-CommentParam:

@Data
public class CommentParam extends PageParam implements Serializable {

    /**
     * 文章ID
     */
    private String articleId;

    /**
     * 用户Id
     */
    private String userId;

    /**
     * 关键字
     */
    private String keyWord;

    /**
     * 昵称(模糊查询)
     */
    private String nickName;

    /**
     * 开始时间
     */
    private Long startTime;

    /**
     * 结束时间
     */
    private Long endTime;
}

Controller:

@RestController
@RequestMapping("mongo")
@Slf4j
public class CommentController {
	 @Resource
    private CommentRepository commentRepository;

    @Resource
    private MongoTemplate mongoTemplate;
    
    @GetMapping
    public Object findCommentList(CommentParam param) {
        // 如果只是单一条件
        // Query query = new Query(Criteria.where("nickName").is("查询内容"));
        // List<Comment> comment = mongoTemplate.find(query, Comment.class);
        // Comment one = mongoTemplate.findOne(query, Comment.class);
        
        // 多条件匹配,复杂查询
        Criteria criteria = new Criteria();
        if (param.getArticleId() != null) {
            criteria.and("articleId").is(param.getArticleId());
        }
        if (param.getUserId() != null) {
            criteria.and("userId").is(param.getUserId());
        }
        if (param.getNickName() != null) {
            //模糊匹配
            //右匹配
            //  Pattern pattern = Pattern.compile("^.*"+param.getKeyWord()+"$", Pattern.CASE_INSENSITIVE);
            //左匹配
            // Pattern pattern = Pattern.compile("^"+param.getKeyWord()+".*$", Pattern.CASE_INSENSITIVE);
            Pattern pattern = Pattern.compile("^.*" + param.getNickName() + ".*$", Pattern.CASE_INSENSITIVE);
            criteria.and("nickName").regex(pattern);
        }
        if (param.getKeyWord() != null) {
            //模糊匹配 - 或查询
            Pattern pattern = Pattern.compile("^.*" + param.getKeyWord() + ".*$", Pattern.CASE_INSENSITIVE);
            criteria.orOperator(Criteria.where("nickName").regex(pattern), Criteria.where("content").regex(pattern));
        }
        if (param.getEndTime() != null && param.getStartTime() != null) {
            criteria.andOperator(Criteria.where("createTime").gte(param.getStartTime()), Criteria.where("createTime").lte(param.getEndTime()));
        }

        Query query = new Query();
        // 添加查询条件
        query.addCriteria(criteria);
        // 排序-时间降序,userId生效
        query.with(Sort.by(Sort.Order.desc("createTime"), Sort.Order.asc("userId")));
        // 分页
        PageBuilder.startPage(query, param.getPageSize(), param.getPageNum());
        // 查询
        List<Comment> list = mongoTemplate.find(query, Comment.class);
        // 分页参数封装
        return new NewPageBean<>(list, Comment.class, param, mongoTemplate);
    }
}