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

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的服务器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);}
}

相关文章:

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

Spring Boot集成MongoDB 文章目录 Spring Boot集成MongoDB一&#xff1a;使用前的准备1&#xff1a;依赖导入 & 配置2&#xff1a;实体类创建 二&#xff1a;核心 - MongoRepository三&#xff1a;核心 - MongoTemplate1&#xff1a;集合操作2&#xff1a;文档操作(重点)3&…...

计算机网络之运输层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 04 传输层 在网上看到其他人做了相关笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 王道考研 计算机网络笔记 第五章&#xff1a;传输层_王道计算机网络传输层_Baret-H的博客-CSDN博…...

DeepSeekMoE:迈向混合专家语言模型的终极专业化

一、结论写在前面 论文提出了MoE语言模型的DeepSeekMoE架构&#xff0c;目的是实现终极的专家专业化(expert specialization)。通过细粒度的专家分割和共享专家隔离&#xff0c;DeepSeekMoE相比主流的MoE架构实现了显著更高的专家专业化和性能。从较小的2B参数规模开始&#x…...

知识库管理驱动企业知识流动与工作协同创新模式

内容概要 知识库管理在现代企业中扮演着至关重要的角色&#xff0c;其价值不仅体现在知识的积累&#xff0c;还在于通过优质的信息流动促进协作与创新。有效的知识库能够将分散的信息整合为有序、易于访问的资源&#xff0c;为员工提供实时支持&#xff0c;进而提升整体工作效…...

二叉树-堆(补充)

二叉树-堆 1.二叉树的基本特性2.堆2.1.堆的基本概念2.2.堆的实现2.2.1.基本结构2.2.2.堆的初始化2.2.3.堆的销毁2.2.4.堆的插入2.2.5.取出堆顶的数据2.2.6.堆的删除2.2.7.堆的判空2.2.8.堆的数据个数2.2.9.交换2.2.10.打印堆数据2.2.11.堆的创建2.2.12.堆排序2.2.13.完整代码 3…...

Java面试题2025-并发编程基础(多线程、锁、阻塞队列)

并发编程 一、线程的基础概念 一、基础概念 1.1 进程与线程A 什么是进程&#xff1f; 进程是指运行中的程序。 比如我们使用钉钉&#xff0c;浏览器&#xff0c;需要启动这个程序&#xff0c;操作系统会给这个程序分配一定的资源&#xff08;占用内存资源&#xff09;。 …...

【方法论】ChatGPT与DeepSeek的联合应用,提升工作效率的新解决方案

标题&#xff1a;ChatGPT与DeepSeek的联合应用&#xff0c;提升工作效率的新解决方案 【表格】ChatGPT与DeepSeek联合应用流程 阶段工具主要任务优势备注初稿生成ChatGPT基于用户输入生成初步内容高效、快速生成内容&#xff0c;适应多种主题适合生成长篇文章、报告、分析等验…...

RoboMaster- RDK X5能量机关实现案例(一)识别

作者&#xff1a;SkyXZ CSDN&#xff1a;https://blog.csdn.net/xiongqi123123 博客园&#xff1a;https://www.cnblogs.com/SkyXZ 在RoboMaster的25赛季&#xff0c;我主要负责了能量机关的视觉方案开发&#xff0c;目前整体算法已经搭建完成&#xff0c;实际方案上我使用的上…...

5分钟带你获取deepseek api并搭建简易问答应用

目录 1、获取api 2、获取base_url和chat_model 3、配置模型参数 方法一&#xff1a;终端中临时将加入 方法二&#xff1a;创建.env文件 4、 配置client 5、利用deepseek大模型实现简易问答 deepseek-v3是截止博文撰写之日&#xff0c;无论是国内还是国际上发布的大模型中…...

Ikigai是什么

Ikigai&#xff08;生き甲斐&#xff09; 是一个日语词语&#xff0c;意思是“生活的意义”或“生命的价值所在”。它是一种关于人生意义的哲学概念&#xff0c;源自日本文化&#xff0c;强调通过找到自己热爱、擅长、社会需要以及能带来经济回报的交集来实现幸福和满足感。 I…...

基于PyQt设计的智能停车管理系统

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】设计意义【4】国内外研究现状【6】摘要1.2 设计思路1.3 系统功能总结1.4 开发工具的选择【1】VSCODE【2】python【3】ptqt【4】HyperLPR31.5 参考文献二、安装Python环境1.1 环境介绍**1.2 Python版本介…...

Flink (十二) :Table API SQL (一) 概览

Apache Flink 有两种关系型 API 来做流批统一处理&#xff1a;Table API 和 SQL。Table API 是用于 Scala 和 Java 语言的查询API&#xff0c;它可以用一种非常直观的方式来组合使用选取、过滤、join 等关系型算子。Flink SQL 是基于 Apache Calcite 来实现的标准 SQL。无论输入…...

MySQL知识点总结(十三)

执行逻辑备份要具备哪些条件&#xff0c;其优缺点在哪。 逻辑备份是温备&#xff0c;创建逻辑备份文件时&#xff0c;MySQL服务器必须处于运行状态&#xff0c;其他应用程序在逻辑备份期间不能修改但可以执行读取操作。逻辑备份会把表结构和数据转换为SQL语句保存。 逻辑备份…...

ACL-2024 | 具身智能空间理解能力几何?EmbSpatial-Bench:视觉语言大模型在具身任务中空间理解水平测试基准

作者&#xff1a;Mengfei Du, Binhao Wu, Zejun Li, Xuanjing Huang, Zhongyu Wei 单位&#xff1a;复旦大学数据科学学院&#xff0c;复旦大学计算机科学学院 论文标题&#xff1a;EmbSpatial-Bench: Benchmarking Spatial Understanding for Embodied Tasks with Large Vis…...

动手学图神经网络(6):利用图神经网络进行点云分类

利用图神经网络进行点云分类 引言 在本教程中,大家将学习使用图神经网络(Graph Neural Networks, GNN)进行点云分类的基本工具。给定一组对象或点集的数据集,将这些对象嵌入到一个特征空间中,使得它们在特定任务下能够分类。将原始点云作为神经网络的输入,让网络学习捕…...

Ollama+DeepSeek本地大模型部署

1、Ollama 官网&#xff1a;https://ollama.com/ Ollama可以干什么&#xff1f; 可以快速在本地部署和管理各种大语言模型&#xff0c;操作命令和dokcer类似。 mac安装ollama&#xff1a; # 安装ollama brew install ollama# 启动ollama服务&#xff08;默认11434端口&#xf…...

docker安装Redis:docker离线安装Redis、docker在线安装Redis、Redis镜像下载、Redis配置、Redis命令

一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull redis:7.4.0 2、离线包下载 两种方式&#xff1a; 方式一&#xff1a; -&#xff09;在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -&#xff09;导出 # 导出镜像…...

HTML 标题

HTML 标题 引言 HTML&#xff08;超文本标记语言&#xff09;是构建网页的基础&#xff0c;而标题则是网页中不可或缺的元素。标题不仅能够帮助用户快速了解网页内容&#xff0c;还能够对搜索引擎优化&#xff08;SEO&#xff09;产生重要影响。本文将详细介绍HTML标题的用法…...

记录 | MaxKB创建本地AI智能问答系统

目录 前言一、重建MaxKBStep1 复制路径Step2 删除MaxKBStep3 创建数据存储文件夹Step4 重建 二、创建知识库Step1 新建知识库Step2 下载测试所用的txtStep3 上传本地文档Step4 选择模型补充智谱的API Key如何获取 Step5 查看是否成功 三、创建应用Step1 新建应用Step2 配置AI助…...

Linux 非阻塞IO

Linux 非阻塞IO 1. fcntl() 在Linux操作系统中&#xff0c;fcntl() 是一个用于操作文件描述符的系统调用。它提供了多种功能&#xff0c;包括控制文件描述符的属性、管理文件锁定、设置文件的非阻塞模式等。 本文只截取了用于IO模型的 fcntl() 部分内容&#xff0c; fcntl() …...

美国本科申请文书PS写作中的注意事项

在完成了introduction之后&#xff0c;便可进入到main body的写作之中。美国本科申请文书PS的写作不同于学术论文写作&#xff0c;要求你提出论点进行论证之类。PS更多的注重对你自己的经历或者motivation的介绍和描述。而这一描述过程只能通过对你自己的过往的经历的展现才能体…...

Qt文件操作

目录 一、文件操作相关类 1.QFile 2.QFileInfo 3.QTextStream 4.QDataStream 5.QDir 6.QFileSystemWatcher 7.QTemporaryFile 二、文件操作示例 1.文本文件操作 2.目录操作 3.二进制文件操作 一、文件操作相关类 1.QFile QFile类用于文件的创建、读写、复制、删除…...

赚钱的究极认识

1、赚钱的本质是提供了价值或者价值想象 价值&#xff1a; 比如小米手机靠什么&#xff1f;“性价比”&#xff0c;什么饥饿营销&#xff0c;创新&#xff0c;用户参与&#xff0c;生态供应链&#xff0c;品牌这些不能说不重要&#xff0c;但是加在一起都没有“性价比”这3字重…...

【项目】基于Qt开发的音乐播放软件

目录 项目介绍 项目概述 界面开发 界面分析 创建工程 主界面布局设计 窗口主框架设计 界面美化 主窗口设定 添加图片资源 head处理 播放控制区处理 自定义控件 BtForm 推荐页面 自定义CommonPage 自定义ListItemBox 自定义MusicSlider 自定义VolumeTool 音…...

week08_文本匹配任务

1、文本匹配任务概述 狭义&#xff1a; 给定一组文本&#xff0c;判断其是否语义相似 今天天气不错 match 今儿个天不错呀 √ 今天天气不错 match 你的代码有bug 以分值形式给出相似度 今天天气不错 match 今儿个天不错呀 0.9 今天天气不错 match…...

01-01 五元组

[外链图片转存中…(img-8JR8fhPZ-1737855365022)] 01-01 五元组 网络中的五元组&#xff08;5-Tuple&#xff09; 是用于唯一标识一个网络连接或数据流的五个关键参数组合。这五个参数共同定义了数据包的来源、目的地以及传输方式&#xff0c;是网络设备&#xff08;如防火墙…...

5.2 软件需求分析

文章目录 需求分析的意义软件需求的组成需求分析的5个方面需求分析方法 需求分析的意义 需求分析解决软件“做什么”的问题。由于开发人员比较熟悉计算机而不熟悉领域业务&#xff0c;用户比较熟悉领域业务而不熟悉计算机&#xff0c;双方需要通过交流&#xff0c;制定出完整、…...

OpenCV:在图像中添加噪声(瑞利、伽马、脉冲、泊松)

目录 简述 1. 瑞利噪声 2. 伽马噪声 3. 脉冲噪声 4. 泊松噪声 总结 相关阅读 OpenCV&#xff1a;在图像中添加高斯噪声、胡椒噪声-CSDN博客 OpenCV&#xff1a;高通滤波之索贝尔、沙尔和拉普拉斯-CSDN博客 OpenCV&#xff1a;图像处理中的低通滤波-CSDN博客 OpenCV&…...

进程池的制作(linux进程间通信,匿名管道... ...)

目录 一、进程间通信的理解 1.为什么进程间要通信 2.如何进行通信 二、匿名管道 1.管道的理解 2.匿名管道的使用 3.管道的五种特性 4.管道的四种通信情况 5.管道缓冲区容量 三、进程池 1.进程池的理解 2.进程池的制作 四、源码 1.ProcessPool.hpp 2.Task.hpp 3…...

C++:多继承习题3

题目内容&#xff1a; 声明一个时间类Time&#xff0c;时间类中有3个私有数据成员(Hour&#xff0c;Minute&#xff0c;Second)和两个公有成员函数(SetTime和PrintTime)。要求&#xff1a; &#xff08;1&#xff09; SetTime根据传递的3个参数为对象设置时间&#xff1b; &a…...

数论问题75

命题&#xff0c;证明:存在K∈N&#xff0c;使得对于每个n∈N&#xff0c;Kx2^n1都是合数。 证明:设n2^m&#xff0c;当m0&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;4时&#xff0c;a(m)2^(2^m)1都是素数。 a(0)213&#xff0c;a(1)2^215&#xff0c;a(2)2^4117&…...

Baklib引领数字化内容管理转型提升企业运营效率

内容概要 在数字化迅速发展的背景下&#xff0c;企业正面临着前所未有的内容管理挑战。传统的内容管理方式已难以适应如今的信息爆炸&#xff0c;企业需要更加高效、智能的解决方案以应对复杂的数据处理需求。Baklib作为行业的先锋&#xff0c;以其创新技术对数字化内容管理进…...

2025年AI手机集中上市,三星Galaxy S25系列上市

2025年被认为是AI手机集中爆发的一年&#xff0c;各大厂商都会推出搭载人工智能的智能手机。三星Galaxy S25系列全球上市了。 三星Galaxy S25系列包含S25、S25和S25 Ultra三款机型&#xff0c;起售价为800美元&#xff08;约合人民币5800元&#xff09;。全系搭载骁龙8 Elite芯…...

Vue2官网教程查漏补缺学习笔记 - 3Vue实例4模板语法5计算属性监听器

3 Vue实例 3.1 创建一个 Vue 实例 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的&#xff1a; var vm new Vue({// 选项 })虽然没有完全遵循 MVVM 模型&#xff0c;但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变…...

2025年数学建模美赛:A题分析(1)Testing Time: The Constant Wear On Stairs

2025年数学建模美赛 A题分析&#xff08;1&#xff09;Testing Time: The Constant Wear On Stairs 2025年数学建模美赛 A题分析&#xff08;2&#xff09;楼梯磨损分析模型 2025年数学建模美赛 A题分析&#xff08;3&#xff09;楼梯使用方向偏好模型 2025年数学建模美赛 A题分…...

WPS数据分析000007

目录 一、分列 智能分列 出生日期 数值转换 公式不运算 二、数据对比 离职员工 新入职员工 都在职的员工 三、合并计算 四、拆分表格 合并表格 一、分列 智能分列 出生日期 数据求和 文本型数字左对齐&#xff1b;数值型数字右对齐 数值转换 方式一&#xff1a; 方…...

oracle 19C RAC打补丁到19.26

oracle 19CRAC打补丁到19.26 本文只保留简介命令和每个命令大概的用时&#xff0c;方便大家直接copy使用&#xff0c;并了解每个命令的预期时间&#xff0c;减少命令执行期的等待焦虑。 1.本次所需的补丁如下 p6880880_190000_Linux-x86-64.zip &#xff08;.43的opatch&…...

动手学图神经网络(8):在消息传递中定制聚合操作

在消息传递中定制聚合操作 安装Pytorch和PyG # 安装所需的包 import os import torch os.environ[TORCH] = torch.__version__# 以下是安装命令,实际运行时可取消注释 #!pip install -q torch-scatter -f https://data.pyg.org/whl/torch-${TORCH}.html #!pip install -q to…...

MySQL安装教程

一、下载 点开下面的链接&#xff1a;下载地址 点击Download 就可以下载对应的安装包了, 安装包如下: 二、解压 下载完成后我们得到的是一个压缩包&#xff0c;将其解压&#xff0c;我们就可以得到MySQL 8.0.34 的软件本体了(就是一个文件夹)&#xff0c;我们可以把它放在你想…...

信息学奥赛一本通 1390:食物链【NOI2001】| 洛谷 P2024 [NOI2001] 食物链

【题目链接】 ybt 1390&#xff1a;食物链【NOI2001】 洛谷 P2024 [NOI2001] 食物链 【题目考点】 1. 种类并查集 2. 带权并查集 【解题思路】 解法1&#xff1a;种类并查集 已知有三类动物A、B、C。A吃B&#xff0c;B吃C&#xff0c;C吃A。 对于B类动物来说&#xff0c…...

Linux网络之序列化和反序列化

目录 序列化和反序列化 上期我们学习了基于TCP的socket套接字编程接口&#xff0c;并实现了一个TCP网络小程序&#xff0c;本期我们将在此基础上进一步延伸学习&#xff0c;实现一个网络版简单计算器。 序列化和反序列化 在生活中肯定有这样一个情景。 上图大家肯定不陌生&a…...

【Django教程】用户管理系统

Get Started With Django User Management 开始使用Django用户管理 By the end of this tutorial, you’ll understand that: 在本教程结束时&#xff0c;您将了解&#xff1a; Django’s user authentication is a built-in authentication system that comes with pre-conf…...

万字长文总结前端开发知识---JavaScriptVue3Axios

JavaScript学习目录 一、JavaScript1. 引入方式1.1 内部脚本 (Inline Script)1.2 外部脚本 (External Script) 2. 基础语法2.1 声明变量2.2 声明常量2.3 输出信息 3. 数据类型3.1 基本数据类型3.2 模板字符串 4. 函数4.1 具名函数 (Named Function)4.2 匿名函数 (Anonymous Fun…...

React基础

前言 &#xff08;2021年笔记&#xff09;补充记录 React基础 前言React讲义一、create-react-app二、关于React1、React的起源和发展2、React与传统MVC的关系3、React高性能的体现&#xff1a;虚拟DOM4、React的特点和优势 三、编写第一个react应用程序四、元素与组件1、函数…...

读书笔记:《华为突围ERP封锁全纪实》

文章背景&#xff1a; 2019年5月&#xff0c;华为被美国制裁&#xff0c;其ERP系统面临断供风险。ERP系统是企业核心管理软件&#xff0c;一旦中断&#xff0c;华为的全球业务将陷入瘫痪。面对这一生死存亡的危机&#xff0c;华为启动了“突围”计划&#xff0c;历经数年艰苦奋…...

Linux的udev详解、安装和使用(dev下的设备每次开机的名称不固定怎么办?)

前言&#xff08;问题与需求&#xff09;&#xff1a; 在传统的devfs 1&#xff1a;设备映射的不确定&#xff1a;一个设备多次加载设备的设备文件可能不同&#xff0c;比如一个hub有可能是ttyUSB0或ttyUSB2或ttyUSB3 2&#xff1a;devfs没有足够的主辅设备号&#xff0c;当设…...

单向循环链表的概念+单向循环链表的结点插入+单向循环链表的结点删除+程序设计与笔试题分析

单向循环链表的原理与应用 思考&#xff1a;对于单向链表而言&#xff0c;想要遍历链表&#xff0c;则必须从链表的首结点开始进行遍历&#xff0c;请问有没有更简单的方案实现链表中的数据的增删改查&#xff1f; 回答&#xff1a;是有的&#xff0c;可以使用单向循环的链表…...

【蓝桥杯嵌入式入门与进阶】2.与开发板之间破冰:初始开发板和原理图2

个人主页&#xff1a;Icomi 专栏地址&#xff1a;蓝桥杯嵌入式组入门与进阶 大家好&#xff0c;我是一颗米&#xff0c;本篇专栏旨在帮助大家从0开始入门蓝桥杯并且进阶&#xff0c;若对本系列文章感兴趣&#xff0c;欢迎订阅我的专栏&#xff0c;我将持续更新&#xff0c;祝你…...

Jetson Xavier NX 安装 CUDA 支持的 PyTorch 指南

本指南将帮助开发者完成在 Jetson Xavier NX 上安装 CUDA 支持的 PyTorch。 安装方法 在 Jetson 上安装 Pytorch 只有两种方法。 一种是直接安装他人已经编译好的 PyTorch 轮子&#xff1b;一种是自己从头开始开始构建 PyTorch 轮子并且安装。 使用轮子安装 可以从我的 Gi…...

“harmony”整合不同平台的单细胞数据之旅

其实在Seurat v3官方网站的Vignettes中就曾见过该算法&#xff0c;但并没有太多关注&#xff0c;直到看了北大张泽民团队在2019年10月31日发表于Cell的《Landscap and Dynamics of Single Immune Cells in Hepatocellular Carcinoma》&#xff0c;为了同时整合两类数据&#xf…...