当前位置: 首页 > article >正文

Spring Boot - 数据库集成05 - 集成MongoDB

Spring Boot集成MongoDB

文章目录

  • Spring Boot集成MongoDB
    • 一:使用前的准备
      • 1:依赖导入 & 配置
      • 2:实体类创建
    • 二:核心 - MongoRepository
    • 三:核心 - MongoTemplate
      • 1:集合操作
      • 2:文档操作(重点)
      • 3:索引操作
    • 四:基础项目演示
      • 1:repository介绍
      • 2:template介绍
      • 3:ExampleMatcher介绍

使用Spring Data 框架都是按照面向对象思想操作用于的工具。

使用Spring Data Mongodb 也是使用面向对象的方式进行操作MongoDB,省略了使用Mongodb的Java客户端API把Document转换为实体类的过程

在这里插入图片描述

一:使用前的准备

1:依赖导入 & 配置

<!-- spring-boot-data-mongodb -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring:
  data:
    mongodb:
      host: localhost # mongodb的服务器ip
      port: 27017 # 服务端口号,默认是27017
      # username: cui # 用户名
      # password: 123456 # 密码
      database: test # 当前项目连接的数据库;
      # url: mongodb://cui:123456@192.168.229.137:27017/test # url是前五项的合集写法
      
      # authentication-database: test # 认证的数据库(连接用的账号,不在连接的库下时使用);
      # auto-index-creation: on # 是否自动创建索引的配置;

      # ---------- 副本集写法,必须是写在url中
      # connect:连接模式,指定为replicaSet代表连接副本集群;
      # slaveOk:从节点是否可读,为true表示可读,执行语句时,读操作自动发往从节点;
      # replicaSet:副本集群的名称,这里为cui;
      # --> mongodb://cui:123456 <- 用户名:密码
      # uri: mongodb://cui:123456@192.168.229.137:27018,192.168.229.137:27019,192.168.229.137:27020/test?connect=replicaSet&slaveOk=true&replicaSet=cui

      # ---------- 分片集群配置,必须写在url中,不需要跟option参数
      # ---------- 只需要配置所有mongo所在的IP、端口即可
      # uri: mongodb://cui:123456@192.168.229.137:27024,192.168.229.137:27025/test

2:实体类创建

package com.cui.springmongodemo.entity;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serializable;

/**
 * <p>
 * 功能描述:实体类
 * ---------- 各个注解作用范围和含义如下
 * /@Document:作用于类上面,被该注解修饰的类,会和MongoDB中的集合相映射,如果类名和集合名不一致,可以通过collection参数来指定。
 * /@Id:标识一个字段为主键,可以加在任意字段上,但如果该字段不为_id,每次插入需要自己生成全局唯一的主键;如果不设置@Id主键,MongoDB会默认插入一个_id值来作为主键。
 * /@Transient:被该注解修饰的属性,在CRUD操作发生时,SpringData会自动将其忽略,不会被传递给MongoDB。
 * /@Field:作用于普通属性上,如果Java属性名和MongoDB字段名不一致,可以通过该注解来做别名映射。
 * /@DBRef:一般用来修饰“嵌套文档”字段,主要用于关联另一个文档。
 * /@Indexed:可作用于任意属性上,被该注解修饰的属性,如果MongoDB中还未创建索引,在第一次插入时,SpringData会默认为其创建一个普通索引。
 * /@CompoundIndex:作用于类上,表示创建复合索引,可以通过name参数指定索引名,def参数指定组成索引的字段及排序方式。
 * /@GeoSpatialIndexed、@TextIndexed:和上面的@Indexed注解作用相同,前者代表空间索引,后者代表全文索引。
 * </p>
 *
 * @author cui haida
 * @date 2023/11/25/16:43
 */
@Data
@Document(collection = "animals") // @Document -> 作用于类上面,被该注解修饰的类,会和MongoDB中的集合相映射
public class Animals implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id // 标识一个字段为主键,可以加在任意字段上,但如果该字段不为_id,每次插入需要自己生成全局唯一的主键
    private Integer id;

    private String name;

    private Integer age;

    private String color;

    private Food food;
}
package com.cui.springmongodemo.entity;

import lombok.Data;

import java.io.Serializable;

/**
 * <p>
 * 功能描述:演示对象
 * </p>
 *
 * @author cui haida
 * @date 2023/11/25/16:45
 */
@Data
public class Food implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private String grade;
}

二:核心 - MongoRepository

基本操作都在这里,只要你的命名足够规范

自定义方法名开头 + 方法名开头跟的关键字 + 字段名 + 字段名称后面可以接的关键字

在这里插入图片描述

package com.cui.springmongodemo.repository;

import com.cui.springmongodemo.entity.Animals;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.DeleteQuery;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 通过标准名写法自动生成语句
 *
 * @author cui haida
 * @date 2023/11/25/16:47
 */
@Repository
public interface AnimalsRepository extends MongoRepository<Animals, Integer> {
    // 查询指定年龄的动物的数量
    Integer countByAge(Integer age);

    // 对动物的名称进行全模糊查询
    List<Animals> findByNameLike(String keyword);

    // 查询时同时满足年龄和姓名两个条件
    Animals findByAgeAndName(int age, String name);

    // 查询满足颜色、年龄其中任一条件的所有动物
    List<Animals> findByColorOrAge(String color, int age);

    // 查询第一个带有颜色的动物
    Animals findFirstByColorNotNull();

    // 查询年龄大于等于指定岁数的动物
    List<Animals> findByAgeGreaterThanEqual(int age);

    // 对id进行多值查询
    List<Animals> findByIdIn(List<Integer> ids);

    // 查询指定颜色的动物,并按照年龄降序返回
    List<Animals> findByColorOrderByAgeDesc(String color);

    // 查询年龄小于指定岁数的前三条数据
    List<Animals> findTop3ByAgeLessThan(int age);

    // 分页查询
    Page<Animals> findByAgeNotNull(Pageable pageable);

    // 注解式写法,自定义
    // @Query这用于自定义查询语句,其中声明根据name字段进行查询
    // ?0表示方法参数的索引(占位符),此处的0表示第一个参数name
    
    // 除此之外,还有另外几个注解,分别对应其他操作:
    // @Update:用于自定义更新语句的注解;
    // @DeleteQuery:用于自定义删除语句的注解;
    // @CountQuery:用于自定义统计语句的注解;
    // @ExistsQuery:用于自定义查询语句,但执行后只返回是否存在满足条件的数据,并不返回具体的文档;
    // @Aggregation:用于自定义聚合管道语句的注解;
    @Query("{'age': {$lt: ?0}}") // age要比传入的参数小才满足条件
    List<Animals> queryXxx(int age);
}
package com.cui.springmongodemo.service;

import com.cui.springmongodemo.entity.Animals;
import org.springframework.data.domain.Page;

import java.util.List;

/**
 * <p>
 * 功能描述:
 * </p>
 *
 * @author cui haida
 * @date 2023/11/25/16:49
 */
public interface AnimalsService {
    /**
     * 新增保存动物
     * @param animals 动物对象
     */
    void save(Animals animals);

    /**
     * 通过id删除
     * @param id 要删除的对象的id
     */
    void deleteById(Integer id);

    /**
     * 更新动物
     * @param animals 要更新的动物
     */
    void update(Animals animals);

    /**
     * 通过id返回指定的动物
     * @param id 要查询的动物的id
     * @return 查询到的动物对象
     */
    Animals findById(Integer id);

    /**
     * 拿到集合中的所有的动物对象
     * @return 所有的动物对象
     */
    List<Animals> findAll();

    /**
     * 查询年龄小于指定岁数的前三条数据
     * @param age 年龄
     * @return 年龄小于指定岁数的前三条数据
     */
    List<Animals> findTop3(int age);

    /**
     * 分页测试
     * @param pageNumber 页码
     * @param pageSize 页大小
     * @return 分页结果
     */
    Page<Animals> findByAgeNotNull(int pageNumber, int pageSize);

    /**
     * 自定义注解测试
     * @param age 年龄
     * @return 返回 < 输入年龄的动物集合
     */
    List<Animals> queryXxx(int age);

    /**
     * 事务测试
     */
    void mongoTransaction();
}
package com.cui.springmongodemo.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.cui.springmongodemo.entity.Animals;
import com.cui.springmongodemo.repository.AnimalsRepository;
import com.cui.springmongodemo.service.AnimalsService;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;

/**
 * <p>
 * 功能描述:逻辑层实现类
 * </p>
 *
 * @author cui haida
 * @date 2023/11/25/16:53
 */
@Service
@Slf4j
public class AnimalsServiceImpl implements AnimalsService {

    @Resource
    private AnimalsRepository animalsRepository;

    @Resource
    private MongoClient mongoClient;

    @Override
    public void save(Animals animals) {
        animalsRepository.save(animals);
    }

    @Override
    public void deleteById(Integer id) {
        animalsRepository.deleteById(id);
    }

    @Override
    public void update(Animals animals) {
        animalsRepository.save(animals);
    }

    @Override
    public Animals findById(Integer id) {
        Optional<Animals> animals = animalsRepository.findById(id);
        if (animals.isPresent()) {
            return animals.get();
        } else {
            log.info("没有找到对应的实体");
            return null;
        }
    }

    @Override
    public List<Animals> findAll() {
        return animalsRepository.findAll();
    }

    @Override
    public List<Animals> findTop3(int age) {
        return animalsRepository.findTop3ByAgeLessThan(age);
    }

    @Override
    public Page<Animals> findByAgeNotNull(int pageNumber, int pageSize) {
        PageRequest pageRequest = PageRequest.of(pageNumber, pageSize);
        return animalsRepository.findByAgeNotNull(pageRequest);
    }

    @Override
    public List<Animals> queryXxx(int age) {
        return animalsRepository.queryXxx(age);
    }

    // 其实很少用mongo的事务机制
    @Override
    public void mongoTransaction() {
        // 1.先通过mongoClient开启一个session会话
        ClientSession session = mongoClient.startSession();
        try{
            // 2.通过session开启事务
            session.startTransaction();

            // 3.创建一个实体对象
            Animals animals = new Animals();
            animals.setId(222);
            animals.setName("白白");
            animals.setColor("白色");
            animals.setAge(1);

            // 4.通过mongoClient获取集合对象
            MongoCollection<Document> collection = mongoClient.getDatabase("test").getCollection("animals");

            // 5.通过集合对象提供的insert方法插入数据
            collection.insertOne(
                    session,
                    Document.parse(JSONObject.toJSONString(animals))
            );

            // 6.模拟执行异常
            int n = 100 / 0;

            // 7.如果执行到这里,说明执行没报错,提交事务
            session.commitTransaction();
        } catch (Exception e) {
            // 8.如果进入了catch,说明出现异常,回滚事务
            session.abortTransaction();
            e.printStackTrace();
        }
        // 9.关闭前面开启的session会话
        session.close();
    }
}

三:核心 - MongoTemplate

1:集合操作

一:创建集合

// 要创建集合的名称,collectionOptions -> 参数
mongoTemplate.createCollection(collectionName, [collectionOptions]);

public Integer createCollectionFixedSize(String collectionName, Long size, Long maxDocCount) {
    CollectionOptions collectionOptions = CollectionOptions.empty()
        .capped() // 创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档
        .size(size) // 固定集合指定一个最大值,以千字节计(KB),如果 capped 为 true,也需要指定该字段
        .maxDocuments(maxDocCount); // 指定固定集合中包含文档的最大数量
    mongoTemplate.createCollection(collectionName, collectionOptions);
    return mongoTemplate.collectionExists(collectionName) ? 1 : 0;
}

二:查询集合

// 查询所有的集合的名称
mongoTemplate.getCollectionNames();

// 指定集合是否存在
mongoTemplate.collectionExists(collectionName);

三:删除集合

// 删除指定的集合
mongoTemplate.getCollection(collectionName).drop();

2:文档操作(重点)

一:插入文档(insert)

insert方法返回值是新增的Document对象,里面包含了新增后_id的值。

如果集合不存在会自动创建集合。

通过Spring Data MongoDB还会给集合中多加一个_class的属性,存储新增时Document对应Java中类的全限定路径。

这么做为了查询时能把Document转换为Java中的类型。

// 单个插入
User newUser = mongoTemplate.insert(user, COLLECTION_NAME);
// 插入多个
Collection<User> newUserList = mongoTemplate.insert(userList, COLLECTION_NAME);

二:存储文档(save)

在Mongodb中无论是使用客户端API还是使用Spring Data,更新返回结果一定是受影响行数。

if -> 更新后的结果和更新前的结果是相同,返回0。

如果使用save方法,要求所有的属性的值都给到了,否则只能使用update方法

User newUser = mongoTemplate.save(user, COLLECTION_NAME);

三:查询文档(criteria + query + find)

// findAll
mongoTemplate.findAll(User.class, COLLECTION_NAME);
// find by id
mongoTemplate.findById(id, User.class, COLLECTION_NAME);
// 其他常规的Criteria
Criteria sex = Criteria.where("sex").is(user.getSex()); // 准则1
Criteria age = Criteria.where("age").is(user.getAge()); // 准则2
// Criteria criteria = Criteria.where("age").gt(minAge).lte(maxAge); 可能有其他类型的条件
Criteria criteria = new Criteria().andOperator(age, sex); // 准则拼接,and
query = new Query(criteria); // 准则封装到条件中
mongoTemplate.find(query, User.class, COLLECTION_NAME);

四:更新文档(upsert & save & update)

🎉 save也可以看做是一种更新

// 创建条件对象【指明要更新谁】
Criteria criteria = Criteria.where("age").is(30);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
// 创建更新对象,并设置更新的内容
Update update = new Update().set("age", 33).set("name", "zhangsansan");
// upsert没有找到匹配查询的文档,则创建并插入一个新文档
UpdateResult result = mongoTemplate.upsert(query, update, User.class, COLLECTION_NAME);
// mongoTemplate.updateFirst
// mongoTemplate.updateMulti

五:删除文档(remove)

// 创建条件对象
Criteria criteria = Criteria.where("age").is(age).and("sex").is(sex);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
// 执行删除查找到的匹配的全部文档信息
DeleteResult result = mongoTemplate.remove(query, COLLECTION_NAME);
// mongoTemplate.findAndRemove(query, User.class, COLLECTION_NAME);
// mongoTemplate.findAllAndRemove(query, User.class, COLLECTION_NAME);

六:分页操作(query with + PageRequest)

PageRequest是Pageable接口的实现类。里面有protected的构造方法和名称为of的静态方法。

  • PageRequest.of(page,size)
  • PageRequest.of(page,size,Sort) 先排序后分页
  • PageRequest.of(page,size,Direction,properties) 排序后分页
Criteria criteria = Criteria.where("age").is(age);
Query query = new Query(criteria);
int pageIndex = 0;
int pageSize = 2;
query.with(PageRequest.of(pageIndex, pageSize, Sort.Direction.DESC, "age")); // 先通过age倒序排序后分页
List<User> list = mongoTemplate.find(query, User.class);

七:聚合操作(Aggregation newAggregation + stage aggregation )

比较复杂,但是核心就是组装每一个stage,然后封装到newAggregation中

Aggregation aggregation = Aggregation.newAggregation(
    // stage1:分组 -> 基于年龄字段分组,接着统计每组数量,并为统计字段取别名
    Aggregation.group("age").count().as("count"),
    // stage2:基于分组后的_id字段(原age)字段排序(升序)
    Aggregation.sort(Sort.Direction.ASC, "_id")
);
return mongoTemplate.aggregate(aggregation, "animals", Map.class);
package com.cui.springmongodemo.service;

import com.cui.springmongodemo.entity.Animals;
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;

import java.util.List;
import java.util.Map;

/**
 * <p>
 * 功能描述:
 * </p>
 *
 * @author cui haida
 * @date 2023/11/25/18:34
 */
public interface MongoTemplateService {

    /**
     * 聚合操作测试
     */
    AggregationResults<Map> aggOp();

    /**
     * 聚合操作测试
     */
    AggregationResults<Map> aggOpOfMaxAndMin();

    /* 案例二:
     * 1:过滤掉food为空,以及age小于3岁的数据
     * 2:接着按food.grade分组,
     * 3:并求出每组的平均年龄、以及输出每组第一个、最后一个、所有动物姓名,
     * 4:最后按照平均年龄升序排列
     * */
    AggregationResults<Map> aggOp2();

    /* 案例三:
    * 1:先过滤掉food为空的数据
    * 2:基于food.grade分组
    * 3:且保留原文档,并输出分组字段值、原文档的name、age字段、原文档在各分组中的下标
    * 4:同时要支持分页功能
    * */
    List<AggregationResults<Map>> aggOp3();
}
package com.cui.springmongodemo.service.impl;

import com.cui.springmongodemo.entity.Animals;
import com.cui.springmongodemo.service.MongoTemplateService;
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
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 org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 功能描述:
 * </p>
 *
 * @author cui haida
 * @date 2023/11/25/18:35
 */
@Service
public class MongoTemplateServiceImpl implements MongoTemplateService {

    @Resource
    private MongoTemplate mongoTemplate;

    @Override
    public AggregationResults<Map> aggOp() {
        Aggregation aggregation = Aggregation.newAggregation(
            // 基于年龄字段分组,接着统计每组数量,并为统计字段取别名
            Aggregation.group("age").count().as("count"),
            // 基于分组后的_id字段(原age)字段排序(升序)
            Aggregation.sort(Sort.Direction.ASC, "_id")
        );
        return mongoTemplate.aggregate(aggregation, "animals", Map.class);
    }

    @Override
    public AggregationResults<Map> aggOpOfMaxAndMin() {
        Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.group("color")
            .max("age").as("max_age")
            .min("age").as("min_age")
            .avg("age").as("avg_age"),
            Aggregation.sort(Sort.Direction.DESC, "max_age")
        );

        return mongoTemplate.aggregate(aggregation, "animals", Map.class);
    }

    /* 案例二:
     * 1:过滤掉food为空,以及age小于3岁的数据
     * 2:接着按food.grade分组,
     * 3:并求出每组的平均年龄、以及输出每组第一个、最后一个、所有动物姓名,
     * 4:最后按照平均年龄升序排列
     * */
    @Override
    public AggregationResults<Map> aggOp2() {
        // 过滤掉food为空,以及age小于3岁的数据的条件
        Criteria criteria = Criteria.where("food").exists(true).and("age").gte(3);
        
        Aggregation aggregation = Aggregation.newAggregation(
            // 1:match先进行过滤
            Aggregation.match(criteria),
            // 2:分组 + 聚合函数
            Aggregation.group("food.grade") // 通过food.grade进行分组,然后统计每一个分组的信息
            .avg("age").as("avg_age")
            .first("name").as("first_name")
            .last("name").as("last_name")
            .push("name").as("names"),
            // 排序
            Aggregation.sort(Sort.Direction.ASC, "age")
        );
        return mongoTemplate.aggregate(aggregation, "animals", Map.class);
    }

    /* 案例三:
     * 1:先过滤掉food为空的数据
     * 2:基于food.grade分组
     * 3:且保留原文档,并输出分组字段值、原文档的name、age字段、原文档在各分组中的下标
     * 4:同时要支持分页功能
     * */
    @Override
    public List<AggregationResults<Map>> aggOp3() {
        // 1.每页的数据量为2条
        int pageSize = 2;
        // 2.过滤掉food字段为空的条件对象
        Criteria criteria = Criteria.where("food").exists(true);

        List<AggregationResults<Map>> ans = new ArrayList<>();

        // 3.用for循环模拟三个分页请求
        for (int pageNumber = 1; pageNumber <= 3; pageNumber++) {
            Aggregation aggregation = Aggregation.newAggregation(
                // 4.过滤阶段:传入构建好的条件对象
                Aggregation.match(criteria),
                // 5.分组阶段:
                // 1:按food.grade分组
                // 2:$$ROOT代表引用原文档,并放入pandas数组
                Aggregation.group("food.grade").push("$$ROOT").as("pandas"),
                // 6.拆分阶段:
                // 将前面每个分组的pandas数组拆成一个个文档(index:下标,true:防丢失)
                Aggregation.unwind("pandas", "index", true),
                // 7.投影阶段:
                // 去掉部分字段不显示,只显示_id(原food.grade)、name、age、index字段
                Aggregation.project("_id", "pandas.name", "pandas.age", "index"),
                // 8.分页阶段:使用skip、limit来区分读取不同页的数据
                Aggregation.skip((long) (pageNumber - 1) * pageNumber),
                Aggregation.limit(pageSize)
            );

            AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "animals", Map.class);
            ans.add(results);
        }
        return ans;
    }
}

嵌套文档操作(update.push & update.pull)

// ========== 插入嵌套文档示例 ============
import org.springframework.beans.factory.annotation.Autowired;
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 com.mongodb.client.model.Projections;
 
public class NestedDocumentService {
 
    @Autowired
    private MongoTemplate mongoTemplate;
 
    public void insertNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedDocument) {
        // 获取父文档
        Query query = new Query(Criteria.where("_id").is(parentId));
        query.fields().include("_id");
        Object parentDocument = mongoTemplate.findOne(query, parentCollectionName);
 
        // 确保父文档存在
        if (parentDocument != null) {
            // 创建嵌套文档的字段路径
            String nestedFieldPath = nestedCollectionName + ".$";
 
            // 创建更新对象,并使用push操作符插入嵌套文档
            Update update = new Update();
            update.push(nestedFieldPath, nestedDocument);
 
            // 更新父文档
            mongoTemplate.updateFirst(query, update, parentCollectionName);
        }
    }
}
// ========== 删除嵌套文档示例 ============
public void deleteNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedId) {
    // 获取父文档
    Query query = new Query(Criteria.where("_id").is(parentId));
    query.fields().include("_id");
    Object parentDocument = mongoTemplate.findOne(query, parentCollectionName);

    // 确保父文档存在
    if (parentDocument != null) {
        // 创建嵌套文档的字段路径
        String nestedFieldPath = nestedCollectionName + ".$";

        // 创建更新对象,并使用pull操作符删除嵌套文档
        Update update = new Update();
        update.pull(nestedFieldPath, nestedId);

        // 更新父文档
        mongoTemplate.updateFirst(query, update, parentCollectionName);
    }
}
}

对于嵌套文档的更新:

假设有一个名为users的集合,其中的文档包含嵌套的address字段,我们想要更新一个特定用户的地址。

@Autowired
private MongoTemplate mongoTemplate;

public void updateUserAddress(String userId, String street, String city) {
    Query query = new Query(Criteria.where("_id").is(userId));
    Update update = new Update();
    // 使用点表示法访问嵌套文档的字段
    update.set("address.street", street);
    update.set("address.city", city);

    // 更新第一个匹配的文档
    mongoTemplate.updateFirst(query, update, "users");
}

九:ExampleMatcher匹配器:了解

package com.cui.springmongodemo.service;

import com.cui.springmongodemo.entity.Animals;

import java.util.List;

/**
 * <p>
 * 功能描述:ExampleMatcher相关操作介绍
 * </p>
 *
 * @author cui haida
 * @date 2023/11/26/7:32
 */
public interface ExampleMatcherService {
    List<Animals> findAnimalsByName(Animals animals);
}
package com.cui.springmongodemo.service.impl;

import com.cui.springmongodemo.entity.Animals;
import com.cui.springmongodemo.service.ExampleMatcherService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
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.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 * 功能描述:ExampleMatcher匹配器测试
 * --------- ExampleMatcher提供了三个方法用于创建匹配器:
 * // 创建一个默认的ExampleMatcher实例(底层调用了matchingAll()方法)
 * static ExampleMatcher matching();
 * // 创建一个匹配所有属性(字段)的ExampleMatcher实例
 * static ExampleMatcher matchingAll();
 * // 创建一个匹配任意属性(字段)的ExampleMatcher实例
 * static ExampleMatcher matchingAny();
 * 
 * ---------- 自定义匹配规则的方法:
 * // 为指定字段设置自定义的匹配规则
 * ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
 * // 为字符串设置自定义的匹配规则
 * ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher);
 * // 设置匹配时忽略大小写
 * ExampleMatcher withIgnoreCase();
 * // 设置匹配时包含空值字段
 * ExampleMatcher withIncludeNullValues();
 * // 设置匹配时忽略空值字段
 * ExampleMatcher withIgnoreNullValues();
 * // 为指定字段设置“字段值转换器”
 * ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer);
 * // 设置匹配时要排除的字段值
 * ExampleMatcher withIgnorePaths(String... ignoredPaths);
 * 
 * ------------ GenericPropertyMatcher类型,而该类中提供了一些内置规则方法
 * // 设置匹配时,指定字段的值,必须包含给定值
 * public GenericPropertyMatcher contains();
 * // 设置匹配时,指定字段的值,必须以给定值开头
 * public GenericPropertyMatcher startsWith();
 * // 设置匹配时,指定字段的值,必须以给定值结尾
 * public GenericPropertyMatcher endsWith();
 * // 设置匹配时,指定字段的值,必须与给定值完全匹配
 * public GenericPropertyMatcher exact();
 * // 设置匹配时,指定字段的值,必须符合给定的正则表达式
 * public GenericPropertyMatcher regex();
 * // 设置匹配时,指定字段的值会区分大小写
 * public GenericPropertyMatcher caseSensitive();
 * // 设置匹配时,指定字段的值不区分大小写
 * public GenericPropertyMatcher ignoreCase();
 *
 * </p>
 *
 * @author cui haida
 * @date 2023/11/26/7:34
 */
@Service
@Slf4j
public class ExampleMatcherServiceImpl implements ExampleMatcherService {

    @Resource
    private MongoTemplate mongoTemplate;

    @Override
    public List<Animals> findAnimalsByName(Animals animals) {
        // 创建了一个匹配器
        ExampleMatcher matcher = ExampleMatcher.matching()
                // 匹配规则一:忽略大小写
                .withIgnoreCase()
                // 匹配规则二:匹配字段:name字段,匹配关系:包含关系
                .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains());
        // matcher + 参数 -> 创建出匹配器实例
        Example<Animals> example = Example.of(animals, matcher);
        // 将其传入到条件对象中
        Criteria criteria = new Criteria().alike(example);
        // mongoTemplate
        return mongoTemplate.find(Query.query(criteria), Animals.class);
    }
}

3:索引操作

一:创建索引(indexOps + ensureIndex)

// 在name字段创建一个唯一正序索引
Index index = new Index().on("name", Sort.Direction.ASC).unique();
// ensureIndex
mongoTemplate.indexOps("animals").ensureIndex(index);

二:查询索引(indexOps + getIndexInfo)

public List<IndexInfo> getIndexInfo(String collectionName) {
    return mongoTemplate.indexOps(collectionName).getIndexInfo();
}

三:删除索引(indexOps + dropIndex)

// 删除指定的索引
mongoTemplate.getCollection(COLLECTION_NAME).dropIndex(indexName);
// 删除指定的索引方式2
mongoTemplate.indexOps(COLLECTION_NAME).dropIndex(indexName);

// 删除全部的索引
mongoTemplate.getCollection(COLLECTION_NAME).dropIndexes();

四:基础项目演示

项目结构如下

在这里插入图片描述
依赖导入和配置,实体类在上面已经给出。

1:repository介绍

package com.cui.mongo_demo.service;

import com.cui.mongo_demo.entity.model.Animal;
import org.springframework.data.domain.Page;

import java.util.List;

/**
 * service层接口
 * @author cui haida
 * 2025/1/27
 */
public interface AnimalService {
    /**
     * 新增保存动物
     * @param animal 动物对象
     */
    void save(Animal animal);

    /**
     * 通过id删除
     * @param id 要删除的对象的id
     */
    void deleteById(Integer id);

    /**
     * 更新动物
     * @param animal 要更新的动物
     */
    void update(Animal animal);

    /**
     * 通过id返回指定的动物
     * @param id 要查询的动物的id
     * @return 查询到的动物对象
     */
    Animal findById(Integer id);

    /**
     * 拿到集合中的所有的动物对象
     * @return 所有的动物对象
     */
    List<Animal> findAll();

    /**
     * 查询年龄小于指定岁数的前三条数据
     * @param age 年龄
     * @return 年龄小于指定岁数的前三条数据
     */
    List<Animal> findTop3(int age);

    /**
     * 分页测试
     * @param pageNumber 页码
     * @param pageSize 页大小
     * @return 分页结果
     */
    Page<Animal> findByAgeNotNull(int pageNumber, int pageSize);

    /**
     * 自定义注解测试
     * @param age 年龄
     * @return 返回 < 输入年龄的动物集合
     */
    List<Animal> queryByDefine(int age);

    /**
     * 事务测试
     */
    void mongoTransaction();
}
package com.cui.mongo_demo.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.cui.mongo_demo.entity.model.Animal;
import com.cui.mongo_demo.repository.AnimalRepository;
import com.cui.mongo_demo.service.AnimalService;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;

/**
 * @author cui haida
 * 2025/1/27
 */
@Service
public class AnimalServiceImpl implements AnimalService {

    @Resource
    private AnimalRepository animalRepository;

    @Resource
    private MongoClient mongoClient;

    @Override
    public void save(Animal animal) {
        animalRepository.save(animal);
    }

    @Override
    public void deleteById(Integer id) {
        animalRepository.deleteById(id);
    }

    @Override
    public void update(Animal animal) {
        animalRepository.save(animal);
    }

    @Override
    public Animal findById(Integer id) {
        Optional<Animal> animal = animalRepository.findById(id);
        return animal.orElse(null);
    }

    @Override
    public List<Animal> findAll() {
        return animalRepository.findAll();
    }

    @Override
    public List<Animal> findTop3(int age) {
        return animalRepository.findTop3ByAgeLessThan(age);
    }

    @Override
    public Page<Animal> findByAgeNotNull(int pageNumber, int pageSize) {
        PageRequest pageRequest = PageRequest.of(pageNumber, pageSize);
        return animalRepository.findByAgeNotNull(pageRequest);
    }

    @Override
    public List<Animal> queryByDefine(int age) {
        return animalRepository.queryByDefine(age);
    }

    @Override
    public void mongoTransaction() {
        // 1.先通过mongoClient开启一个session会话
        ClientSession session = mongoClient.startSession();
        try{
            // 2.通过session开启事务
            session.startTransaction();

            // 3.创建一个实体对象
            Animal animals = new Animal();
            animals.setId(222);
            animals.setName("白白");
            animals.setColor("白色");
            animals.setAge(1);

            // 4.通过mongoClient获取集合对象
            MongoCollection<Document> collection = mongoClient.getDatabase("test").getCollection("animals");

            // 5.通过集合对象提供的insert方法插入数据
            collection.insertOne(
                    session,
                    Document.parse(JSONObject.toJSONString(animals))
            );

            // 6.模拟执行异常
            int n = 100 / 0;

            // 7.如果执行到这里,说明执行没报错,提交事务
            session.commitTransaction();
        } catch (Exception e) {
            // 8.如果进入了catch,说明出现异常,回滚事务
            session.abortTransaction();
            e.printStackTrace();
        }
        // 9.关闭前面开启的session会话
        session.close();
    }
}

2:template介绍

package com.cui.mongo_demo.service;

import com.cui.mongo_demo.entity.model.Animal;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.index.IndexInfo;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author cui haida
 * 2025/1/27
 */
public interface TemplateService {

    /**
     * 创建集合
     *
     * @param collectionName 集合名称
     */
    public void createCollection(String collectionName);

    /**
     * 创建集合并设置固定大小
     *
     * @param collectionName 集合名称
     * @param size           集合大小
     * @param maxDocCount    最大集合文档数量
     * @return 集合创建结果
     */
    public Integer createCollectionFixedSize(String collectionName, Long size, Long maxDocCount);


    /**
     * 获取所有集合名称
     *
     * @return 集合名称列表
     */
    public Set<String> getAllCollectionNames();


    /**
     * 判断集合是否存在
     *
     * @param collectionName 集合名称
     * @return 集合是否存在
     */
    public boolean existCollectionByName(String collectionName);


    /**
     * 删除集合
     *
     * @param collectionName 集合名称
     */
    public void dropCollection(String collectionName);

    /**
     * 插入文档
     *
     * @param animal 文档
     * @return 插入结果
     */
    public Animal insertDocument(Animal animal);

    /**
     * 批量插入文档
     *
     * @param animals 文档列表
     * @return 插入结果
     */
    public List<Animal> batchInsertDocument(List<Animal> animals);

    /**
     * 保存文档
     *
     * @param animal 文档
     * @return 保存结果
     */
    public Animal saveAnimal(Animal animal);

    /**
     * 查询所有文档
     *
     * @return 文档列表
     */
    public List<Animal> findAll();

    /**
     * 根据id查询
     *
     * @param id 文档id
     * @return 文档
     */
    public Animal findById(Integer id);

    /**
     * 根据条件查询
     *
     * @param sex 性别
     * @param age 年龄
     * @return 文档列表
     */
    public List<Animal> findByCondition(String sex, Integer age);

    /**
     * 根据年龄更新
     *
     * @param oldAge 旧年龄
     * @param newAge 新年龄
     * @param newName 新名字
     */
    public void updateByAge(Integer oldAge, Integer newAge, String newName);

    /**
     * 根据年龄和性别删除
     *
     * @param age 年龄
     * @param sex 性别
     */
    public void removeByAgeAndSex(Integer age, String sex);

    /**
     * 分页查询
     *
     * @param age    年龄
     * @param pageNum 页码
     * @param pageSize 页大小
     * @return 文档列表
     */
    public List<Animal> pageByAge(Integer age, Integer pageNum, Integer pageSize);

    /**
     * 聚合查询
     *
     * @return 聚合结果
     */
    public AggregationResults<Map> aggByAgeAndCount();

    /**
     * 聚合查询
     *
     * @return 聚合结果
     */
    public AggregationResults<Map> aggOpMinAndMax();

    /**
     * 聚合查询
     *
     * @return 聚合结果
     */
    public AggregationResults<Map> aggOpComplex(int bottomAge);

    /**
     * 聚合查询
     *
     * @return 聚合结果
     */
    public List<AggregationResults<Map>> aggOpComplexOfPage(int bottomAge);


    /**
     * 插入嵌套文档
     *
     * @param parentCollectionName 父集合名称
     * @param parentId 父文档id
     * @param nestedCollectionName 嵌套集合名称
     * @param nestedDocument 嵌套文档
     */
    public void insertNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedDocument);

    /**
     * 删除嵌套文档
     *
     * @param parentCollectionName 父集合名称
     * @param parentId 父文档id
     * @param nestedCollectionName 嵌套集合名称
     * @param nestedId 嵌套文档id
     */
    public void deleteNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedId);


    /**
     * 创建索引
     * @param collectionName 集合名称
     */
    public void createIndex(String collectionName, String fieldName);

    /**
     * 获取索引信息
     * @param collectionName 集合名称
     * @return 索引信息
     */
    public List<IndexInfo> getIndexInfo(String collectionName);

    /**
     * 删除索引
     * @param collectionName 集合名称
     * @param fieldName 索引对应的字段的名称
     */
    public void removeIndex(String collectionName, String fieldName);

    /**
     * 删除所有索引
     * @param collectionName 集合名称
     */
    public void removeAllIndex(String collectionName);
}
package com.cui.mongo_demo.service.impl;

import com.cui.mongo_demo.entity.model.Animal;
import com.cui.mongo_demo.service.TemplateService;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.CollectionOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexInfo;
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 org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author cui haida
 * 2025/1/27
 */
@Service
public class TemplateServiceImpl implements TemplateService {

    private final String COLLECTION_NAME = "animal";

    private final MongoTemplate mongoTemplate;

    public TemplateServiceImpl(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @Override
    public void createCollection(String collectionName) {
        mongoTemplate.createCollection(collectionName);
    }

    @Override
    public Integer createCollectionFixedSize(String collectionName, Long size, Long maxDocCount) {
        // .capped() // 创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档
        // .size(size) // 固定集合指定一个最大值,以千字节计(KB),如果 capped 为 true,也需要指定该字段
        // .maxDocuments(maxDocCount); // 指定固定集合中包含文档的最大数量
        CollectionOptions collectionOptions = CollectionOptions.empty()
                .capped()
                .size(size)
                .maxDocuments(maxDocCount);
        mongoTemplate.createCollection(collectionName, collectionOptions);
        return mongoTemplate.collectionExists(collectionName) ? 1 : 0;
    }

    @Override
    public Set<String> getAllCollectionNames() {
        return mongoTemplate.getCollectionNames();
    }

    @Override
    public boolean existCollectionByName(String collectionName) {
        return mongoTemplate.collectionExists(collectionName);
    }

    @Override
    public void dropCollection(String collectionName) {
        mongoTemplate.getCollection(collectionName).drop();
    }

    @Override
    public Animal insertDocument(Animal animal) {
        return mongoTemplate.insert(animal, COLLECTION_NAME);
    }

    @Override
    public List<Animal> batchInsertDocument(List<Animal> animals) {
        Collection<Animal> as = mongoTemplate.insert(animals, COLLECTION_NAME);
        return new ArrayList<>(as);
    }

    @Override
    public Animal saveAnimal(Animal animal) {
        return mongoTemplate.save(animal, COLLECTION_NAME);
    }

    @Override
    public List<Animal> findAll() {
        return mongoTemplate.findAll(Animal.class, COLLECTION_NAME);
    }

    @Override
    public Animal findById(Integer id) {
        return mongoTemplate.findById(id, Animal.class, COLLECTION_NAME);
    }

    @Override
    public List<Animal> findByCondition(String sex, Integer age) {
        // 1:构建条件-准则
        Criteria sexOfCriteria = Criteria.where("sex").is(sex);
        Criteria ageOfCriteria = Criteria.where("age").is(age);
        // 指定两个准则的拼接条件为and, 也就是 sex = ${sex} and age = ${age}
        Criteria criteria = new Criteria().andOperator(sexOfCriteria, ageOfCriteria);
        // 2:基于准则构建查询条件
        Query query = new Query(criteria);
        // 3:通过find方法查询数据,返回List查询结果
        return mongoTemplate.find(query, Animal.class, COLLECTION_NAME);
    }

    @Override
    public void updateByAge(Integer oldAge, Integer newAge, String newName) {
        // 1:指定更新准则
        Criteria criteria = Criteria.where("age").is(oldAge);
        Query query = new Query(criteria);
        // 2:指定更新内容
        Update update = new Update();
        update.set("age", newAge);
        update.set("name", newName);
        // 3:通过updateMulti方法更新数据, 还有updateFirst和upsert
        mongoTemplate.updateMulti(query, update, Animal.class, COLLECTION_NAME);
    }

    @Override
    public void removeByAgeAndSex(Integer age, String sex) {
        // 1:指定删除条件
        Criteria criteria = Criteria.where("age").is(age).and("sex").is(sex);
        Query query = new Query(criteria);
        mongoTemplate.remove(query, Animal.class, COLLECTION_NAME);
    }

    @Override
    public List<Animal> pageByAge(Integer age, Integer pageNum, Integer pageSize) {
        // 1:构建查询条件
        Criteria criteria = Criteria.where("age").is(age);
        Query query = new Query(criteria);
        // 加入分页条件, 并通过年龄进行倒排(先进行倒排再分页)
        query.with(PageRequest.of(pageNum, pageSize, Sort.Direction.DESC, "age"));
        // 2:通过find方法查询数据,返回List查询结果
        return mongoTemplate.find(query, Animal.class, COLLECTION_NAME);
    }

    @Override
    public AggregationResults<Map> aggByAgeAndCount() {
        // 1. 构建聚合信息
        Aggregation aggregation = Aggregation.newAggregation(
                // 基于年龄字段分组,接着统计每组数量,并为统计字段取别名
                Aggregation.group("age")
                        .count().as("count"),
                // 基于分组后的_id字段(原age)字段排序(升序)
                Aggregation.sort(Sort.Direction.ASC, "_id")
        );
        // 2. 执行聚合操作,返回结果, 返回结果为Map类型
        // 第一个参数是聚合条件,第二个参数是作用的集合名称,第三个参数是返回类型
        return mongoTemplate.aggregate(aggregation, "animals", Map.class);
    }

    @Override
    public AggregationResults<Map> aggOpMinAndMax() {
        // 1. 构建聚合条件 Aggregation.newAggregation( condition1, condition2...)
        Aggregation aggregation = Aggregation.newAggregation(
                // 通过颜色进行分组,然后统计最大值、最小值、平均值。
                Aggregation.group("color")
                        .max("age").as("max_age")
                        .min("age").as("min_age")
                        .avg("age").as("avg_age"),
                // 排序,按照最大值进行倒排
                Aggregation.sort(Sort.Direction.DESC, "max_age")
        );
        // 2. 执行聚合操作,返回结果
        return mongoTemplate.aggregate(aggregation, "animals", Map.class);
    }

    @Override
    public AggregationResults<Map> aggOpComplex(int bottomAge) {
        // 1. 过滤掉food为空,以及age小于3岁的数据的条件
        Criteria criteria =
                Criteria.where("food").exists(true).and("age").gte(3);

        // 2. 构建聚合条件
        Aggregation aggregation = Aggregation.newAggregation(
                // 1:match先进行过滤
                Aggregation.match(criteria),
                // 2:分组 + 聚合函数
                Aggregation.group("food.grade")
                        // 通过food.grade进行分组,然后统计每一个分组的信息
                        .avg("age").as("avg_age")
                        .first("name").as("first_name")
                        .last("name").as("last_name")
                        .push("name").as("names"),
                // 排序
                Aggregation.sort(Sort.Direction.ASC, "age")
        );

        // 3. 执行聚合操作,返回结果
        return mongoTemplate.aggregate(aggregation, "animals", Map.class);
    }

    @Override
    public List<AggregationResults<Map>> aggOpComplexOfPage(int bottomAge) {
        // 1.每页的数据量为2条
        int pageSize = 2;
        // 2.过滤掉food字段为空的条件对象
        Criteria criteria = Criteria.where("food").exists(true);
        // 初始化存储结果的数据结构
        List<AggregationResults<Map>> ans = new ArrayList<>();

        // 3.用for循环模拟分页查询
        for (int pageNumber = 1; pageNumber <= 3; pageNumber++) {
            Aggregation aggregation = Aggregation.newAggregation(
                    // 4.过滤阶段:传入构建好的条件对象
                    Aggregation.match(criteria),
                    // 5.分组阶段:
                    // 1:按food.grade分组
                    // 2:$$ROOT代表引用原文档,并放入pandas数组(// 将源文档信息推入(push...as)pandas数组)
                    Aggregation.group("food.grade")
                            .push("$$ROOT").as("pandas"),
                    // 6.拆分阶段:
                    // 将前面每个分组的pandas数组拆成一个个文档(index:下标,true:防丢失)
                    Aggregation.unwind("pandas", "index", true),
                    // 7.投影阶段:
                    // 去掉部分字段不显示,只显示_id(原food.grade)、name、age、index字段
                    Aggregation.project("_id", "pandas.name", "pandas.age", "index"),
                    // 8.分页阶段:使用skip、limit来区分读取不同页的数据
                    Aggregation.skip((long) (pageNumber - 1) * pageNumber),
                    Aggregation.limit(pageSize)
            );

            AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "animals", Map.class);
            ans.add(results);
        }
        return ans;
    }

    @Override
    public void insertNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedDocument) {
        // 1. 获取父文档
        Query query = new Query(Criteria.where("_id").is(parentId));
        query.fields().include("_id");
        Animal parentDocument = mongoTemplate.findOne(query, Animal.class, parentCollectionName);

        if (parentDocument != null) {
            // 2.如果父文档存在,则插入嵌套文档
            String nestedFieldPath = nestedCollectionName + ".$";

            // 3.创建更新对象,并使用push操作符插入嵌套文档
            Update update = new Update();
            update.push(nestedFieldPath, nestedDocument);

            // 4.更新父文档
            mongoTemplate.updateFirst(query, update, parentCollectionName);
        }
    }

    @Override
    public void deleteNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedId) {
        // 1. 获取父文档
        Query query = new Query(Criteria.where("_id").is(parentId));
        query.fields().include("_id");
        Animal parentDocument = mongoTemplate.findOne(query, Animal.class, parentCollectionName);

        if (parentDocument != null) {
            // 2.如果父文档存在,则删除嵌套文档
            String nestedFieldPath = nestedCollectionName + ".$." + "_id";

            // 3.创建更新对象,并使用pull操作符删除嵌套文档
            Update update = new Update();
            update.pull(nestedFieldPath, nestedId);

            // 4.更新父文档
            mongoTemplate.updateFirst(query, update, parentCollectionName);
        }
    }

    @Override
    public void createIndex(String collectionName, String fieldName) {
        // 1.创建索引信息
        Index index = new Index()
                // 作用在指定字段上,排序方式为正序
                .on(fieldName, Sort.Direction.ASC)
                // 是唯一索引
                .unique();

        // 2.indexOps()方法用于获取集合的索引操作对象,然后调用ensureIndex()方法来创建索引
        mongoTemplate.indexOps(collectionName).ensureIndex(index);
    }

    @Override
    public List<IndexInfo> getIndexInfo(String collectionName) {
        return mongoTemplate.indexOps(collectionName).getIndexInfo();
    }

    @Override
    public void removeIndex(String collectionName, String fieldName) {
        // 删除指定字段上的索引
        mongoTemplate.indexOps(collectionName).dropIndex(fieldName);
    }

    @Override
    public void removeAllIndex(String collectionName) {
        mongoTemplate.indexOps(collectionName).dropAllIndexes();
    }
}

3:ExampleMatcher介绍

package com.cui.mongo_demo.service;

import com.cui.mongo_demo.entity.model.Animal;

import java.util.List;

/**
 * @author cuihaida
 * 2025/1/28
 */
public interface ExampleMatcherService {
    /**
     * 根据名称查询
     * @param animal 查询条件
     * @return 查询结果
     */
    List<Animal> findAnimalsByName(Animal animal);
}
package com.cui.mongo_demo.service.impl;

import com.cui.mongo_demo.entity.model.Animal;
import com.cui.mongo_demo.service.ExampleMatcherService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
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.stereotype.Service;

import java.util.List;

/**
 * <p>
 *  * 功能描述:ExampleMatcher匹配器测试
 *  * --------- ExampleMatcher提供了三个方法用于创建匹配器:
 *  * // 创建一个默认的ExampleMatcher实例(底层调用了matchingAll()方法)
 *  * static ExampleMatcher matching();
 *  * // 创建一个匹配所有属性(字段)的ExampleMatcher实例
 *  * static ExampleMatcher matchingAll();
 *  * // 创建一个匹配任意属性(字段)的ExampleMatcher实例
 *  * static ExampleMatcher matchingAny();
 *  *
 *  * ---------- 自定义匹配规则的方法:
 *  * // 为指定字段设置自定义的匹配规则
 *  * ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
 *  * // 为字符串设置自定义的匹配规则
 *  * ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher);
 *  * // 设置匹配时忽略大小写
 *  * ExampleMatcher withIgnoreCase();
 *  * // 设置匹配时包含空值字段
 *  * ExampleMatcher withIncludeNullValues();
 *  * // 设置匹配时忽略空值字段
 *  * ExampleMatcher withIgnoreNullValues();
 *  * // 为指定字段设置“字段值转换器”
 *  * ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer);
 *  * // 设置匹配时要排除的字段值
 *  * ExampleMatcher withIgnorePaths(String... ignoredPaths);
 *  *
 *  * ------------ GenericPropertyMatcher类型,而该类中提供了一些内置规则方法
 *  * // 设置匹配时,指定字段的值,必须包含给定值
 *  * public GenericPropertyMatcher contains();
 *  * // 设置匹配时,指定字段的值,必须以给定值开头
 *  * public GenericPropertyMatcher startsWith();
 *  * // 设置匹配时,指定字段的值,必须以给定值结尾
 *  * public GenericPropertyMatcher endsWith();
 *  * // 设置匹配时,指定字段的值,必须与给定值完全匹配
 *  * public GenericPropertyMatcher exact();
 *  * // 设置匹配时,指定字段的值,必须符合给定的正则表达式
 *  * public GenericPropertyMatcher regex();
 *  * // 设置匹配时,指定字段的值会区分大小写
 *  * public GenericPropertyMatcher caseSensitive();
 *  * // 设置匹配时,指定字段的值不区分大小写
 *  * public GenericPropertyMatcher ignoreCase();
 *  </p>
 * @author cui haida
 * 2025/1/28
 */
@Service
@Slf4j
public class ExampleMatcherServiceImpl implements ExampleMatcherService {

    private final MongoTemplate mongoTemplate;

    public ExampleMatcherServiceImpl(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @Override
    public List<Animal> findAnimalsByName(Animal animal) {
        // 创建了一个匹配器
        ExampleMatcher matcher = ExampleMatcher.matching()
                // 匹配规则一:忽略大小写
                .withIgnoreCase()
                // 匹配规则二:匹配字段:name字段,匹配关系:包含关系
                .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains());
        // matcher + 参数 -> 创建出匹配器实例
        Example<Animal> example = Example.of(animal, matcher);
        // 将其传入到条件对象中
        Criteria criteria = new Criteria().alike(example);
        // mongoTemplate
        return mongoTemplate.find(Query.query(criteria), Animal.class);
    }
}

http://www.kler.cn/a/524516.html

相关文章:

  • 《使用通道 Transformer 进行多尺度特征融合,引导热图像超分辨率》学习笔记
  • MYSQL 商城系统设计 商品数据表的设计 商品 商品类别 商品选项卡 多表查询
  • DeepSeek理解概率的能力
  • 元素的显示与隐藏
  • 【漫话机器学习系列】064.梯度下降小口诀(Gradient Descent rule of thume)
  • 《 C++ 点滴漫谈: 二十四 》深入 C++ 变量与类型的世界:高性能编程的根基
  • Vue+Echarts 实现青岛自定义样式地图
  • 无用知识研究:对std::common_type以及问号表达式类型的理解
  • 论文阅读笔记:MambaOut: Do We Really Need Mamba for Vision?
  • Unity游戏(Assault空对地打击)开发(2) 基础场景布置
  • 对顾客行为的数据分析:融入2+1链动模式、AI智能名片与S2B2C商城小程序的新视角
  • printf和sprintf区别
  • 深入MapReduce——从MRv1到Yarn
  • fscan全家桶更新:fscan免杀版,可过360、火绒、微步云沙箱,其他的自行测试
  • Elasticsearch的开发工具(Dev Tools)
  • 创建实用PPT演讲者备注的有效方法
  • AI赋能医疗:智慧医疗系统源码与互联网医院APP的核心技术剖析
  • FreeRTOS的任务创建和删除
  • C#语言的并发编程
  • STM32 TIM输入捕获 测量频率
  • F1. Omsk Metro (simple version)
  • 微信小程序高级开发(5):微信小程序手机验证码登录全栈开发指南
  • Node.js 中文编码问题全解析
  • 【deepseek】deepseek-r1本地部署-第三步:下载模型
  • CISCO路由基础全集
  • Unity 粒子特效在UI中使用裁剪效果