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

ops/2025/2/2 16:21:08/

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的服务器ipport: 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 {@Resourceprivate AnimalsRepository animalsRepository;@Resourceprivate MongoClient mongoClient;@Overridepublic void save(Animals animals) {animalsRepository.save(animals);}@Overridepublic void deleteById(Integer id) {animalsRepository.deleteById(id);}@Overridepublic void update(Animals animals) {animalsRepository.save(animals);}@Overridepublic Animals findById(Integer id) {Optional<Animals> animals = animalsRepository.findById(id);if (animals.isPresent()) {return animals.get();} else {log.info("没有找到对应的实体");return null;}}@Overridepublic List<Animals> findAll() {return animalsRepository.findAll();}@Overridepublic List<Animals> findTop3(int age) {return animalsRepository.findTop3ByAgeLessThan(age);}@Overridepublic Page<Animals> findByAgeNotNull(int pageNumber, int pageSize) {PageRequest pageRequest = PageRequest.of(pageNumber, pageSize);return animalsRepository.findByAgeNotNull(pageRequest);}@Overridepublic List<Animals> queryXxx(int age) {return animalsRepository.queryXxx(age);}// 其实很少用mongo的事务机制@Overridepublic 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 {@Resourceprivate MongoTemplate mongoTemplate;@Overridepublic 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);}@Overridepublic 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:最后按照平均年龄升序排列* */@Overridepublic 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:同时要支持分页功能* */@Overridepublic 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 {@Autowiredprivate 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 {@Resourceprivate MongoTemplate mongoTemplate;@Overridepublic 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);// mongoTemplatereturn 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 {@Resourceprivate AnimalRepository animalRepository;@Resourceprivate MongoClient mongoClient;@Overridepublic void save(Animal animal) {animalRepository.save(animal);}@Overridepublic void deleteById(Integer id) {animalRepository.deleteById(id);}@Overridepublic void update(Animal animal) {animalRepository.save(animal);}@Overridepublic Animal findById(Integer id) {Optional<Animal> animal = animalRepository.findById(id);return animal.orElse(null);}@Overridepublic List<Animal> findAll() {return animalRepository.findAll();}@Overridepublic List<Animal> findTop3(int age) {return animalRepository.findTop3ByAgeLessThan(age);}@Overridepublic Page<Animal> findByAgeNotNull(int pageNumber, int pageSize) {PageRequest pageRequest = PageRequest.of(pageNumber, pageSize);return animalRepository.findByAgeNotNull(pageRequest);}@Overridepublic List<Animal> queryByDefine(int age) {return animalRepository.queryByDefine(age);}@Overridepublic 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;}@Overridepublic void createCollection(String collectionName) {mongoTemplate.createCollection(collectionName);}@Overridepublic 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;}@Overridepublic Set<String> getAllCollectionNames() {return mongoTemplate.getCollectionNames();}@Overridepublic boolean existCollectionByName(String collectionName) {return mongoTemplate.collectionExists(collectionName);}@Overridepublic void dropCollection(String collectionName) {mongoTemplate.getCollection(collectionName).drop();}@Overridepublic Animal insertDocument(Animal animal) {return mongoTemplate.insert(animal, COLLECTION_NAME);}@Overridepublic List<Animal> batchInsertDocument(List<Animal> animals) {Collection<Animal> as = mongoTemplate.insert(animals, COLLECTION_NAME);return new ArrayList<>(as);}@Overridepublic Animal saveAnimal(Animal animal) {return mongoTemplate.save(animal, COLLECTION_NAME);}@Overridepublic List<Animal> findAll() {return mongoTemplate.findAll(Animal.class, COLLECTION_NAME);}@Overridepublic Animal findById(Integer id) {return mongoTemplate.findById(id, Animal.class, COLLECTION_NAME);}@Overridepublic 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);}@Overridepublic 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和upsertmongoTemplate.updateMulti(query, update, Animal.class, COLLECTION_NAME);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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;}@Overridepublic 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);}}@Overridepublic 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);}}@Overridepublic 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);}@Overridepublic List<IndexInfo> getIndexInfo(String collectionName) {return mongoTemplate.indexOps(collectionName).getIndexInfo();}@Overridepublic void removeIndex(String collectionName, String fieldName) {// 删除指定字段上的索引mongoTemplate.indexOps(collectionName).dropIndex(fieldName);}@Overridepublic 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;}@Overridepublic 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);// mongoTemplatereturn mongoTemplate.find(Query.query(criteria), Animal.class);}
}

http://www.ppmy.cn/ops/155077.html

相关文章

RabbitMQ---面试题

常见面试题 1.MQ的作用及应用场景 类似问题&#xff1a;项目什么情况下用到了MQ&#xff0c;为什么要用MQ MQ的主要应用场景&#xff0c;消息队列的应用场景&#xff0c;为什么说消息队列可以削峰 首先MQ是一种用来接收和转发消息的队列&#xff0c;常见的应用常见如下&…

【力扣】15.三数之和

AC截图 题目 思路 这道题如果简单的用暴力三重遍历去做&#xff0c;会超时。所以我们思考假如有三个下标&#xff0c;i&#xff0c;l&#xff0c;r 其中i0&#xff08;初始&#xff09;&#xff0c;li1 rnums.size()-1 我们固定nums[i]的值&#xff0c;那么就转换为两数之和…

自定义数据集使用框架的线性回归方法对其进行拟合

代码 import torch import numpy as np import torch.nn as nncriterion nn.MSELoss()data np.array([[-0.5, 7.7],[1.8, 98.5],[0.9, 57.8],[0.4, 39.2],[-1.4, -15.7],[-1.4, -37.3],[-1.8, -49.1],[1.5, 75.6],[0.4, 34.0],[0.8, 62.3]])x_data data[:, 0] y_data data…

边缘计算与ROS结合:如何实现分布式机器人智能决策?

前言 在现代机器人系统中&#xff0c;分布式决策能力正成为实现群体协作任务的关键需求。传统集中式架构存在决策延迟、通信瓶颈以及容错性低等问题&#xff0c;而边缘计算结合 ROS&#xff08;Robot Operating System&#xff09;为分布式机器人智能决策提供了全新的解决方案…

【ArcGIS_Python】使用arcpy脚本将shape数据转换为三维白膜数据

说明&#xff1a; 该专栏之前的文章中python脚本使用的是ArcMap10.6自带的arcpy&#xff08;好几年前的文章&#xff09;&#xff0c;从本篇开始使用的是ArcGIS Pro 3.3版本自带的arcpy&#xff0c;需要注意不同版本对应的arcpy函数是存在差异的 数据准备&#xff1a;准备一个…

实现网站内容快速被搜索引擎收录的方法

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/6.html 实现网站内容快速被搜索引擎收录&#xff0c;是网站运营和推广的重要目标之一。以下是一些有效的方法&#xff0c;可以帮助网站内容更快地被搜索引擎发现和收录&#xff1a; 一、确…

跨境支付领域中常用的英文单词(持续更新)

### **1. 支付方式 (Payment Methods)** 1. Credit Card 2. Debit Card 3. Bank Transfer 4. Wire Transfer 5. PayPal 6. Alipay 7. WeChat Pay 8. Apple Pay 9. Google Pay 10. Cryptocurrency 11. Digital Wallet 12. Mobile Payment 13. Cash on D…

12.udp

12.udp **1. UDP特性****2. UDP编程框架&#xff08;C/S模式&#xff09;****3. UDP发送接收函数****4. UDP编程练习** 1. UDP特性 连接特性&#xff1a;无链接&#xff0c;通信前无需像TCP那样建立连接。可靠性&#xff1a;不可靠&#xff0c;不保证数据按序到达、不保证数据…