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一:使用前的准备1:依赖导入 & 配置2:实体类创建 二:核心 - MongoRepository三:核心 - MongoTemplate1:集合操作2:文档操作(重点)3&…...
计算机网络之运输层
本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 04 传输层 在网上看到其他人做了相关笔记,就不再多余写了,直接参考着学习吧。 王道考研 计算机网络笔记 第五章:传输层_王道计算机网络传输层_Baret-H的博客-CSDN博…...
DeepSeekMoE:迈向混合专家语言模型的终极专业化
一、结论写在前面 论文提出了MoE语言模型的DeepSeekMoE架构,目的是实现终极的专家专业化(expert specialization)。通过细粒度的专家分割和共享专家隔离,DeepSeekMoE相比主流的MoE架构实现了显著更高的专家专业化和性能。从较小的2B参数规模开始&#x…...
知识库管理驱动企业知识流动与工作协同创新模式
内容概要 知识库管理在现代企业中扮演着至关重要的角色,其价值不仅体现在知识的积累,还在于通过优质的信息流动促进协作与创新。有效的知识库能够将分散的信息整合为有序、易于访问的资源,为员工提供实时支持,进而提升整体工作效…...
二叉树-堆(补充)
二叉树-堆 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 什么是进程? 进程是指运行中的程序。 比如我们使用钉钉,浏览器,需要启动这个程序,操作系统会给这个程序分配一定的资源(占用内存资源)。 …...
【方法论】ChatGPT与DeepSeek的联合应用,提升工作效率的新解决方案
标题:ChatGPT与DeepSeek的联合应用,提升工作效率的新解决方案 【表格】ChatGPT与DeepSeek联合应用流程 阶段工具主要任务优势备注初稿生成ChatGPT基于用户输入生成初步内容高效、快速生成内容,适应多种主题适合生成长篇文章、报告、分析等验…...
RoboMaster- RDK X5能量机关实现案例(一)识别
作者:SkyXZ CSDN:https://blog.csdn.net/xiongqi123123 博客园:https://www.cnblogs.com/SkyXZ 在RoboMaster的25赛季,我主要负责了能量机关的视觉方案开发,目前整体算法已经搭建完成,实际方案上我使用的上…...
5分钟带你获取deepseek api并搭建简易问答应用
目录 1、获取api 2、获取base_url和chat_model 3、配置模型参数 方法一:终端中临时将加入 方法二:创建.env文件 4、 配置client 5、利用deepseek大模型实现简易问答 deepseek-v3是截止博文撰写之日,无论是国内还是国际上发布的大模型中…...
Ikigai是什么
Ikigai(生き甲斐) 是一个日语词语,意思是“生活的意义”或“生命的价值所在”。它是一种关于人生意义的哲学概念,源自日本文化,强调通过找到自己热爱、擅长、社会需要以及能带来经济回报的交集来实现幸福和满足感。 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 来做流批统一处理:Table API 和 SQL。Table API 是用于 Scala 和 Java 语言的查询API,它可以用一种非常直观的方式来组合使用选取、过滤、join 等关系型算子。Flink SQL 是基于 Apache Calcite 来实现的标准 SQL。无论输入…...
MySQL知识点总结(十三)
执行逻辑备份要具备哪些条件,其优缺点在哪。 逻辑备份是温备,创建逻辑备份文件时,MySQL服务器必须处于运行状态,其他应用程序在逻辑备份期间不能修改但可以执行读取操作。逻辑备份会把表结构和数据转换为SQL语句保存。 逻辑备份…...
ACL-2024 | 具身智能空间理解能力几何?EmbSpatial-Bench:视觉语言大模型在具身任务中空间理解水平测试基准
作者:Mengfei Du, Binhao Wu, Zejun Li, Xuanjing Huang, Zhongyu Wei 单位:复旦大学数据科学学院,复旦大学计算机科学学院 论文标题:EmbSpatial-Bench: Benchmarking Spatial Understanding for Embodied Tasks with Large Vis…...
动手学图神经网络(6):利用图神经网络进行点云分类
利用图神经网络进行点云分类 引言 在本教程中,大家将学习使用图神经网络(Graph Neural Networks, GNN)进行点云分类的基本工具。给定一组对象或点集的数据集,将这些对象嵌入到一个特征空间中,使得它们在特定任务下能够分类。将原始点云作为神经网络的输入,让网络学习捕…...
Ollama+DeepSeek本地大模型部署
1、Ollama 官网:https://ollama.com/ Ollama可以干什么? 可以快速在本地部署和管理各种大语言模型,操作命令和dokcer类似。 mac安装ollama: # 安装ollama brew install ollama# 启动ollama服务(默认11434端口…...
docker安装Redis:docker离线安装Redis、docker在线安装Redis、Redis镜像下载、Redis配置、Redis命令
一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull redis:7.4.0 2、离线包下载 两种方式: 方式一: -)在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -)导出 # 导出镜像…...
HTML 标题
HTML 标题 引言 HTML(超文本标记语言)是构建网页的基础,而标题则是网页中不可或缺的元素。标题不仅能够帮助用户快速了解网页内容,还能够对搜索引擎优化(SEO)产生重要影响。本文将详细介绍HTML标题的用法…...
记录 | MaxKB创建本地AI智能问答系统
目录 前言一、重建MaxKBStep1 复制路径Step2 删除MaxKBStep3 创建数据存储文件夹Step4 重建 二、创建知识库Step1 新建知识库Step2 下载测试所用的txtStep3 上传本地文档Step4 选择模型补充智谱的API Key如何获取 Step5 查看是否成功 三、创建应用Step1 新建应用Step2 配置AI助…...
Linux 非阻塞IO
Linux 非阻塞IO 1. fcntl() 在Linux操作系统中,fcntl() 是一个用于操作文件描述符的系统调用。它提供了多种功能,包括控制文件描述符的属性、管理文件锁定、设置文件的非阻塞模式等。 本文只截取了用于IO模型的 fcntl() 部分内容, fcntl() …...
美国本科申请文书PS写作中的注意事项
在完成了introduction之后,便可进入到main body的写作之中。美国本科申请文书PS的写作不同于学术论文写作,要求你提出论点进行论证之类。PS更多的注重对你自己的经历或者motivation的介绍和描述。而这一描述过程只能通过对你自己的过往的经历的展现才能体…...
Qt文件操作
目录 一、文件操作相关类 1.QFile 2.QFileInfo 3.QTextStream 4.QDataStream 5.QDir 6.QFileSystemWatcher 7.QTemporaryFile 二、文件操作示例 1.文本文件操作 2.目录操作 3.二进制文件操作 一、文件操作相关类 1.QFile QFile类用于文件的创建、读写、复制、删除…...
赚钱的究极认识
1、赚钱的本质是提供了价值或者价值想象 价值: 比如小米手机靠什么?“性价比”,什么饥饿营销,创新,用户参与,生态供应链,品牌这些不能说不重要,但是加在一起都没有“性价比”这3字重…...
【项目】基于Qt开发的音乐播放软件
目录 项目介绍 项目概述 界面开发 界面分析 创建工程 主界面布局设计 窗口主框架设计 界面美化 主窗口设定 添加图片资源 head处理 播放控制区处理 自定义控件 BtForm 推荐页面 自定义CommonPage 自定义ListItemBox 自定义MusicSlider 自定义VolumeTool 音…...
week08_文本匹配任务
1、文本匹配任务概述 狭义: 给定一组文本,判断其是否语义相似 今天天气不错 match 今儿个天不错呀 √ 今天天气不错 match 你的代码有bug 以分值形式给出相似度 今天天气不错 match 今儿个天不错呀 0.9 今天天气不错 match…...
01-01 五元组
[外链图片转存中…(img-8JR8fhPZ-1737855365022)] 01-01 五元组 网络中的五元组(5-Tuple) 是用于唯一标识一个网络连接或数据流的五个关键参数组合。这五个参数共同定义了数据包的来源、目的地以及传输方式,是网络设备(如防火墙…...
5.2 软件需求分析
文章目录 需求分析的意义软件需求的组成需求分析的5个方面需求分析方法 需求分析的意义 需求分析解决软件“做什么”的问题。由于开发人员比较熟悉计算机而不熟悉领域业务,用户比较熟悉领域业务而不熟悉计算机,双方需要通过交流,制定出完整、…...
OpenCV:在图像中添加噪声(瑞利、伽马、脉冲、泊松)
目录 简述 1. 瑞利噪声 2. 伽马噪声 3. 脉冲噪声 4. 泊松噪声 总结 相关阅读 OpenCV:在图像中添加高斯噪声、胡椒噪声-CSDN博客 OpenCV:高通滤波之索贝尔、沙尔和拉普拉斯-CSDN博客 OpenCV:图像处理中的低通滤波-CSDN博客 OpenCV&…...
进程池的制作(linux进程间通信,匿名管道... ...)
目录 一、进程间通信的理解 1.为什么进程间要通信 2.如何进行通信 二、匿名管道 1.管道的理解 2.匿名管道的使用 3.管道的五种特性 4.管道的四种通信情况 5.管道缓冲区容量 三、进程池 1.进程池的理解 2.进程池的制作 四、源码 1.ProcessPool.hpp 2.Task.hpp 3…...
C++:多继承习题3
题目内容: 声明一个时间类Time,时间类中有3个私有数据成员(Hour,Minute,Second)和两个公有成员函数(SetTime和PrintTime)。要求: (1) SetTime根据传递的3个参数为对象设置时间; &a…...
数论问题75
命题,证明:存在K∈N,使得对于每个n∈N,Kx2^n1都是合数。 证明:设n2^m,当m0,1,2,3,4时,a(m)2^(2^m)1都是素数。 a(0)213,a(1)2^215,a(2)2^4117&…...
Baklib引领数字化内容管理转型提升企业运营效率
内容概要 在数字化迅速发展的背景下,企业正面临着前所未有的内容管理挑战。传统的内容管理方式已难以适应如今的信息爆炸,企业需要更加高效、智能的解决方案以应对复杂的数据处理需求。Baklib作为行业的先锋,以其创新技术对数字化内容管理进…...
2025年AI手机集中上市,三星Galaxy S25系列上市
2025年被认为是AI手机集中爆发的一年,各大厂商都会推出搭载人工智能的智能手机。三星Galaxy S25系列全球上市了。 三星Galaxy S25系列包含S25、S25和S25 Ultra三款机型,起售价为800美元(约合人民币5800元)。全系搭载骁龙8 Elite芯…...
Vue2官网教程查漏补缺学习笔记 - 3Vue实例4模板语法5计算属性监听器
3 Vue实例 3.1 创建一个 Vue 实例 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的: var vm new Vue({// 选项 })虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变…...
2025年数学建模美赛:A题分析(1)Testing Time: The Constant Wear On Stairs
2025年数学建模美赛 A题分析(1)Testing Time: The Constant Wear On Stairs 2025年数学建模美赛 A题分析(2)楼梯磨损分析模型 2025年数学建模美赛 A题分析(3)楼梯使用方向偏好模型 2025年数学建模美赛 A题分…...
WPS数据分析000007
目录 一、分列 智能分列 出生日期 数值转换 公式不运算 二、数据对比 离职员工 新入职员工 都在职的员工 三、合并计算 四、拆分表格 合并表格 一、分列 智能分列 出生日期 数据求和 文本型数字左对齐;数值型数字右对齐 数值转换 方式一: 方…...
oracle 19C RAC打补丁到19.26
oracle 19CRAC打补丁到19.26 本文只保留简介命令和每个命令大概的用时,方便大家直接copy使用,并了解每个命令的预期时间,减少命令执行期的等待焦虑。 1.本次所需的补丁如下 p6880880_190000_Linux-x86-64.zip (.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安装教程
一、下载 点开下面的链接:下载地址 点击Download 就可以下载对应的安装包了, 安装包如下: 二、解压 下载完成后我们得到的是一个压缩包,将其解压,我们就可以得到MySQL 8.0.34 的软件本体了(就是一个文件夹),我们可以把它放在你想…...
信息学奥赛一本通 1390:食物链【NOI2001】| 洛谷 P2024 [NOI2001] 食物链
【题目链接】 ybt 1390:食物链【NOI2001】 洛谷 P2024 [NOI2001] 食物链 【题目考点】 1. 种类并查集 2. 带权并查集 【解题思路】 解法1:种类并查集 已知有三类动物A、B、C。A吃B,B吃C,C吃A。 对于B类动物来说,…...
Linux网络之序列化和反序列化
目录 序列化和反序列化 上期我们学习了基于TCP的socket套接字编程接口,并实现了一个TCP网络小程序,本期我们将在此基础上进一步延伸学习,实现一个网络版简单计算器。 序列化和反序列化 在生活中肯定有这样一个情景。 上图大家肯定不陌生&a…...
【Django教程】用户管理系统
Get Started With Django User Management 开始使用Django用户管理 By the end of this tutorial, you’ll understand that: 在本教程结束时,您将了解: 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基础
前言 (2021年笔记)补充记录 React基础 前言React讲义一、create-react-app二、关于React1、React的起源和发展2、React与传统MVC的关系3、React高性能的体现:虚拟DOM4、React的特点和优势 三、编写第一个react应用程序四、元素与组件1、函数…...
读书笔记:《华为突围ERP封锁全纪实》
文章背景: 2019年5月,华为被美国制裁,其ERP系统面临断供风险。ERP系统是企业核心管理软件,一旦中断,华为的全球业务将陷入瘫痪。面对这一生死存亡的危机,华为启动了“突围”计划,历经数年艰苦奋…...
Linux的udev详解、安装和使用(dev下的设备每次开机的名称不固定怎么办?)
前言(问题与需求): 在传统的devfs 1:设备映射的不确定:一个设备多次加载设备的设备文件可能不同,比如一个hub有可能是ttyUSB0或ttyUSB2或ttyUSB3 2:devfs没有足够的主辅设备号,当设…...
单向循环链表的概念+单向循环链表的结点插入+单向循环链表的结点删除+程序设计与笔试题分析
单向循环链表的原理与应用 思考:对于单向链表而言,想要遍历链表,则必须从链表的首结点开始进行遍历,请问有没有更简单的方案实现链表中的数据的增删改查? 回答:是有的,可以使用单向循环的链表…...
【蓝桥杯嵌入式入门与进阶】2.与开发板之间破冰:初始开发板和原理图2
个人主页:Icomi 专栏地址:蓝桥杯嵌入式组入门与进阶 大家好,我是一颗米,本篇专栏旨在帮助大家从0开始入门蓝桥杯并且进阶,若对本系列文章感兴趣,欢迎订阅我的专栏,我将持续更新,祝你…...
Jetson Xavier NX 安装 CUDA 支持的 PyTorch 指南
本指南将帮助开发者完成在 Jetson Xavier NX 上安装 CUDA 支持的 PyTorch。 安装方法 在 Jetson 上安装 Pytorch 只有两种方法。 一种是直接安装他人已经编译好的 PyTorch 轮子;一种是自己从头开始开始构建 PyTorch 轮子并且安装。 使用轮子安装 可以从我的 Gi…...
“harmony”整合不同平台的单细胞数据之旅
其实在Seurat v3官方网站的Vignettes中就曾见过该算法,但并没有太多关注,直到看了北大张泽民团队在2019年10月31日发表于Cell的《Landscap and Dynamics of Single Immune Cells in Hepatocellular Carcinoma》,为了同时整合两类数据…...