Spring Boot - 数据库集成05 - 集成MongoDB
Spring Boot集成MongoDB
文章目录
- Spring Boot集成MongoDB
- 一:使用前的准备
- 1:依赖导入 & 配置
- 2:实体类创建
- 二:核心 - MongoRepository
- 三:核心 - MongoTemplate
- 1:集合操作
- 2:文档操作(重点)
- 3:索引操作
- 四:基础项目演示
- 1:repository介绍
- 2:template介绍
- 3:ExampleMatcher介绍
使用Spring Data 框架都是按照面向对象思想操作用于的工具。
使用Spring Data Mongodb 也是使用面向对象的方式进行操作MongoDB,省略了使用Mongodb的Java客户端API把Document转换为实体类的过程
一:使用前的准备
1:依赖导入 & 配置
<!-- spring-boot-data-mongodb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring:
data:
mongodb:
host: localhost # mongodb的服务器ip
port: 27017 # 服务端口号,默认是27017
# username: cui # 用户名
# password: 123456 # 密码
database: test # 当前项目连接的数据库;
# url: mongodb://cui:123456@192.168.229.137:27017/test # url是前五项的合集写法
# authentication-database: test # 认证的数据库(连接用的账号,不在连接的库下时使用);
# auto-index-creation: on # 是否自动创建索引的配置;
# ---------- 副本集写法,必须是写在url中
# connect:连接模式,指定为replicaSet代表连接副本集群;
# slaveOk:从节点是否可读,为true表示可读,执行语句时,读操作自动发往从节点;
# replicaSet:副本集群的名称,这里为cui;
# --> mongodb://cui:123456 <- 用户名:密码
# uri: mongodb://cui:123456@192.168.229.137:27018,192.168.229.137:27019,192.168.229.137:27020/test?connect=replicaSet&slaveOk=true&replicaSet=cui
# ---------- 分片集群配置,必须写在url中,不需要跟option参数
# ---------- 只需要配置所有mongo所在的IP、端口即可
# uri: mongodb://cui:123456@192.168.229.137:27024,192.168.229.137:27025/test
2:实体类创建
package com.cui.springmongodemo.entity;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
/**
* <p>
* 功能描述:实体类
* ---------- 各个注解作用范围和含义如下
* /@Document:作用于类上面,被该注解修饰的类,会和MongoDB中的集合相映射,如果类名和集合名不一致,可以通过collection参数来指定。
* /@Id:标识一个字段为主键,可以加在任意字段上,但如果该字段不为_id,每次插入需要自己生成全局唯一的主键;如果不设置@Id主键,MongoDB会默认插入一个_id值来作为主键。
* /@Transient:被该注解修饰的属性,在CRUD操作发生时,SpringData会自动将其忽略,不会被传递给MongoDB。
* /@Field:作用于普通属性上,如果Java属性名和MongoDB字段名不一致,可以通过该注解来做别名映射。
* /@DBRef:一般用来修饰“嵌套文档”字段,主要用于关联另一个文档。
* /@Indexed:可作用于任意属性上,被该注解修饰的属性,如果MongoDB中还未创建索引,在第一次插入时,SpringData会默认为其创建一个普通索引。
* /@CompoundIndex:作用于类上,表示创建复合索引,可以通过name参数指定索引名,def参数指定组成索引的字段及排序方式。
* /@GeoSpatialIndexed、@TextIndexed:和上面的@Indexed注解作用相同,前者代表空间索引,后者代表全文索引。
* </p>
*
* @author cui haida
* @date 2023/11/25/16:43
*/
@Data
@Document(collection = "animals") // @Document -> 作用于类上面,被该注解修饰的类,会和MongoDB中的集合相映射
public class Animals implements Serializable {
private static final long serialVersionUID = 1L;
@Id // 标识一个字段为主键,可以加在任意字段上,但如果该字段不为_id,每次插入需要自己生成全局唯一的主键
private Integer id;
private String name;
private Integer age;
private String color;
private Food food;
}
package com.cui.springmongodemo.entity;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
* 功能描述:演示对象
* </p>
*
* @author cui haida
* @date 2023/11/25/16:45
*/
@Data
public class Food implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String grade;
}
二:核心 - MongoRepository
基本操作都在这里,只要你的命名足够规范
自定义方法名开头 + 方法名开头跟的关键字 + 字段名 + 字段名称后面可以接的关键字
package com.cui.springmongodemo.repository;
import com.cui.springmongodemo.entity.Animals;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.DeleteQuery;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 通过标准名写法自动生成语句
*
* @author cui haida
* @date 2023/11/25/16:47
*/
@Repository
public interface AnimalsRepository extends MongoRepository<Animals, Integer> {
// 查询指定年龄的动物的数量
Integer countByAge(Integer age);
// 对动物的名称进行全模糊查询
List<Animals> findByNameLike(String keyword);
// 查询时同时满足年龄和姓名两个条件
Animals findByAgeAndName(int age, String name);
// 查询满足颜色、年龄其中任一条件的所有动物
List<Animals> findByColorOrAge(String color, int age);
// 查询第一个带有颜色的动物
Animals findFirstByColorNotNull();
// 查询年龄大于等于指定岁数的动物
List<Animals> findByAgeGreaterThanEqual(int age);
// 对id进行多值查询
List<Animals> findByIdIn(List<Integer> ids);
// 查询指定颜色的动物,并按照年龄降序返回
List<Animals> findByColorOrderByAgeDesc(String color);
// 查询年龄小于指定岁数的前三条数据
List<Animals> findTop3ByAgeLessThan(int age);
// 分页查询
Page<Animals> findByAgeNotNull(Pageable pageable);
// 注解式写法,自定义
// @Query这用于自定义查询语句,其中声明根据name字段进行查询
// ?0表示方法参数的索引(占位符),此处的0表示第一个参数name
// 除此之外,还有另外几个注解,分别对应其他操作:
// @Update:用于自定义更新语句的注解;
// @DeleteQuery:用于自定义删除语句的注解;
// @CountQuery:用于自定义统计语句的注解;
// @ExistsQuery:用于自定义查询语句,但执行后只返回是否存在满足条件的数据,并不返回具体的文档;
// @Aggregation:用于自定义聚合管道语句的注解;
@Query("{'age': {$lt: ?0}}") // age要比传入的参数小才满足条件
List<Animals> queryXxx(int age);
}
package com.cui.springmongodemo.service;
import com.cui.springmongodemo.entity.Animals;
import org.springframework.data.domain.Page;
import java.util.List;
/**
* <p>
* 功能描述:
* </p>
*
* @author cui haida
* @date 2023/11/25/16:49
*/
public interface AnimalsService {
/**
* 新增保存动物
* @param animals 动物对象
*/
void save(Animals animals);
/**
* 通过id删除
* @param id 要删除的对象的id
*/
void deleteById(Integer id);
/**
* 更新动物
* @param animals 要更新的动物
*/
void update(Animals animals);
/**
* 通过id返回指定的动物
* @param id 要查询的动物的id
* @return 查询到的动物对象
*/
Animals findById(Integer id);
/**
* 拿到集合中的所有的动物对象
* @return 所有的动物对象
*/
List<Animals> findAll();
/**
* 查询年龄小于指定岁数的前三条数据
* @param age 年龄
* @return 年龄小于指定岁数的前三条数据
*/
List<Animals> findTop3(int age);
/**
* 分页测试
* @param pageNumber 页码
* @param pageSize 页大小
* @return 分页结果
*/
Page<Animals> findByAgeNotNull(int pageNumber, int pageSize);
/**
* 自定义注解测试
* @param age 年龄
* @return 返回 < 输入年龄的动物集合
*/
List<Animals> queryXxx(int age);
/**
* 事务测试
*/
void mongoTransaction();
}
package com.cui.springmongodemo.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.cui.springmongodemo.entity.Animals;
import com.cui.springmongodemo.repository.AnimalsRepository;
import com.cui.springmongodemo.service.AnimalsService;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
/**
* <p>
* 功能描述:逻辑层实现类
* </p>
*
* @author cui haida
* @date 2023/11/25/16:53
*/
@Service
@Slf4j
public class AnimalsServiceImpl implements AnimalsService {
@Resource
private AnimalsRepository animalsRepository;
@Resource
private MongoClient mongoClient;
@Override
public void save(Animals animals) {
animalsRepository.save(animals);
}
@Override
public void deleteById(Integer id) {
animalsRepository.deleteById(id);
}
@Override
public void update(Animals animals) {
animalsRepository.save(animals);
}
@Override
public Animals findById(Integer id) {
Optional<Animals> animals = animalsRepository.findById(id);
if (animals.isPresent()) {
return animals.get();
} else {
log.info("没有找到对应的实体");
return null;
}
}
@Override
public List<Animals> findAll() {
return animalsRepository.findAll();
}
@Override
public List<Animals> findTop3(int age) {
return animalsRepository.findTop3ByAgeLessThan(age);
}
@Override
public Page<Animals> findByAgeNotNull(int pageNumber, int pageSize) {
PageRequest pageRequest = PageRequest.of(pageNumber, pageSize);
return animalsRepository.findByAgeNotNull(pageRequest);
}
@Override
public List<Animals> queryXxx(int age) {
return animalsRepository.queryXxx(age);
}
// 其实很少用mongo的事务机制
@Override
public void mongoTransaction() {
// 1.先通过mongoClient开启一个session会话
ClientSession session = mongoClient.startSession();
try{
// 2.通过session开启事务
session.startTransaction();
// 3.创建一个实体对象
Animals animals = new Animals();
animals.setId(222);
animals.setName("白白");
animals.setColor("白色");
animals.setAge(1);
// 4.通过mongoClient获取集合对象
MongoCollection<Document> collection = mongoClient.getDatabase("test").getCollection("animals");
// 5.通过集合对象提供的insert方法插入数据
collection.insertOne(
session,
Document.parse(JSONObject.toJSONString(animals))
);
// 6.模拟执行异常
int n = 100 / 0;
// 7.如果执行到这里,说明执行没报错,提交事务
session.commitTransaction();
} catch (Exception e) {
// 8.如果进入了catch,说明出现异常,回滚事务
session.abortTransaction();
e.printStackTrace();
}
// 9.关闭前面开启的session会话
session.close();
}
}
三:核心 - MongoTemplate
1:集合操作
一:创建集合
// 要创建集合的名称,collectionOptions -> 参数
mongoTemplate.createCollection(collectionName, [collectionOptions]);
public Integer createCollectionFixedSize(String collectionName, Long size, Long maxDocCount) {
CollectionOptions collectionOptions = CollectionOptions.empty()
.capped() // 创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档
.size(size) // 固定集合指定一个最大值,以千字节计(KB),如果 capped 为 true,也需要指定该字段
.maxDocuments(maxDocCount); // 指定固定集合中包含文档的最大数量
mongoTemplate.createCollection(collectionName, collectionOptions);
return mongoTemplate.collectionExists(collectionName) ? 1 : 0;
}
二:查询集合
// 查询所有的集合的名称
mongoTemplate.getCollectionNames();
// 指定集合是否存在
mongoTemplate.collectionExists(collectionName);
三:删除集合
// 删除指定的集合
mongoTemplate.getCollection(collectionName).drop();
2:文档操作(重点)
一:插入文档(insert)
insert方法返回值是新增的Document对象,里面包含了新增后_id的值。
如果集合不存在会自动创建集合。
通过Spring Data MongoDB还会给集合中多加一个_class的属性,存储新增时Document对应Java中类的全限定路径。
这么做为了查询时能把Document转换为Java中的类型。
// 单个插入
User newUser = mongoTemplate.insert(user, COLLECTION_NAME);
// 插入多个
Collection<User> newUserList = mongoTemplate.insert(userList, COLLECTION_NAME);
二:存储文档(save)
在Mongodb中无论是使用客户端API还是使用Spring Data,更新返回结果一定是受影响行数。
if -> 更新后的结果和更新前的结果是相同,返回0。
如果使用save方法,要求所有的属性的值都给到了,否则只能使用update方法
User newUser = mongoTemplate.save(user, COLLECTION_NAME);
三:查询文档(criteria + query + find)
// findAll
mongoTemplate.findAll(User.class, COLLECTION_NAME);
// find by id
mongoTemplate.findById(id, User.class, COLLECTION_NAME);
// 其他常规的Criteria
Criteria sex = Criteria.where("sex").is(user.getSex()); // 准则1
Criteria age = Criteria.where("age").is(user.getAge()); // 准则2
// Criteria criteria = Criteria.where("age").gt(minAge).lte(maxAge); 可能有其他类型的条件
Criteria criteria = new Criteria().andOperator(age, sex); // 准则拼接,and
query = new Query(criteria); // 准则封装到条件中
mongoTemplate.find(query, User.class, COLLECTION_NAME);
四:更新文档(upsert & save & update)
🎉 save也可以看做是一种更新
// 创建条件对象【指明要更新谁】
Criteria criteria = Criteria.where("age").is(30);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
// 创建更新对象,并设置更新的内容
Update update = new Update().set("age", 33).set("name", "zhangsansan");
// upsert没有找到匹配查询的文档,则创建并插入一个新文档
UpdateResult result = mongoTemplate.upsert(query, update, User.class, COLLECTION_NAME);
// mongoTemplate.updateFirst
// mongoTemplate.updateMulti
五:删除文档(remove)
// 创建条件对象
Criteria criteria = Criteria.where("age").is(age).and("sex").is(sex);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
// 执行删除查找到的匹配的全部文档信息
DeleteResult result = mongoTemplate.remove(query, COLLECTION_NAME);
// mongoTemplate.findAndRemove(query, User.class, COLLECTION_NAME);
// mongoTemplate.findAllAndRemove(query, User.class, COLLECTION_NAME);
六:分页操作(query with + PageRequest)
PageRequest是Pageable接口的实现类。里面有protected的构造方法和名称为of的静态方法。
PageRequest.of(page,size)
PageRequest.of(page,size,Sort)
先排序后分页PageRequest.of(page,size,Direction,properties)
排序后分页
Criteria criteria = Criteria.where("age").is(age);
Query query = new Query(criteria);
int pageIndex = 0;
int pageSize = 2;
query.with(PageRequest.of(pageIndex, pageSize, Sort.Direction.DESC, "age")); // 先通过age倒序排序后分页
List<User> list = mongoTemplate.find(query, User.class);
七:聚合操作(Aggregation newAggregation + stage aggregation )
比较复杂,但是核心就是组装每一个stage,然后封装到newAggregation中
Aggregation aggregation = Aggregation.newAggregation(
// stage1:分组 -> 基于年龄字段分组,接着统计每组数量,并为统计字段取别名
Aggregation.group("age").count().as("count"),
// stage2:基于分组后的_id字段(原age)字段排序(升序)
Aggregation.sort(Sort.Direction.ASC, "_id")
);
return mongoTemplate.aggregate(aggregation, "animals", Map.class);
package com.cui.springmongodemo.service;
import com.cui.springmongodemo.entity.Animals;
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import java.util.List;
import java.util.Map;
/**
* <p>
* 功能描述:
* </p>
*
* @author cui haida
* @date 2023/11/25/18:34
*/
public interface MongoTemplateService {
/**
* 聚合操作测试
*/
AggregationResults<Map> aggOp();
/**
* 聚合操作测试
*/
AggregationResults<Map> aggOpOfMaxAndMin();
/* 案例二:
* 1:过滤掉food为空,以及age小于3岁的数据
* 2:接着按food.grade分组,
* 3:并求出每组的平均年龄、以及输出每组第一个、最后一个、所有动物姓名,
* 4:最后按照平均年龄升序排列
* */
AggregationResults<Map> aggOp2();
/* 案例三:
* 1:先过滤掉food为空的数据
* 2:基于food.grade分组
* 3:且保留原文档,并输出分组字段值、原文档的name、age字段、原文档在各分组中的下标
* 4:同时要支持分页功能
* */
List<AggregationResults<Map>> aggOp3();
}
package com.cui.springmongodemo.service.impl;
import com.cui.springmongodemo.entity.Animals;
import com.cui.springmongodemo.service.MongoTemplateService;
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* <p>
* 功能描述:
* </p>
*
* @author cui haida
* @date 2023/11/25/18:35
*/
@Service
public class MongoTemplateServiceImpl implements MongoTemplateService {
@Resource
private MongoTemplate mongoTemplate;
@Override
public AggregationResults<Map> aggOp() {
Aggregation aggregation = Aggregation.newAggregation(
// 基于年龄字段分组,接着统计每组数量,并为统计字段取别名
Aggregation.group("age").count().as("count"),
// 基于分组后的_id字段(原age)字段排序(升序)
Aggregation.sort(Sort.Direction.ASC, "_id")
);
return mongoTemplate.aggregate(aggregation, "animals", Map.class);
}
@Override
public AggregationResults<Map> aggOpOfMaxAndMin() {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("color")
.max("age").as("max_age")
.min("age").as("min_age")
.avg("age").as("avg_age"),
Aggregation.sort(Sort.Direction.DESC, "max_age")
);
return mongoTemplate.aggregate(aggregation, "animals", Map.class);
}
/* 案例二:
* 1:过滤掉food为空,以及age小于3岁的数据
* 2:接着按food.grade分组,
* 3:并求出每组的平均年龄、以及输出每组第一个、最后一个、所有动物姓名,
* 4:最后按照平均年龄升序排列
* */
@Override
public AggregationResults<Map> aggOp2() {
// 过滤掉food为空,以及age小于3岁的数据的条件
Criteria criteria = Criteria.where("food").exists(true).and("age").gte(3);
Aggregation aggregation = Aggregation.newAggregation(
// 1:match先进行过滤
Aggregation.match(criteria),
// 2:分组 + 聚合函数
Aggregation.group("food.grade") // 通过food.grade进行分组,然后统计每一个分组的信息
.avg("age").as("avg_age")
.first("name").as("first_name")
.last("name").as("last_name")
.push("name").as("names"),
// 排序
Aggregation.sort(Sort.Direction.ASC, "age")
);
return mongoTemplate.aggregate(aggregation, "animals", Map.class);
}
/* 案例三:
* 1:先过滤掉food为空的数据
* 2:基于food.grade分组
* 3:且保留原文档,并输出分组字段值、原文档的name、age字段、原文档在各分组中的下标
* 4:同时要支持分页功能
* */
@Override
public List<AggregationResults<Map>> aggOp3() {
// 1.每页的数据量为2条
int pageSize = 2;
// 2.过滤掉food字段为空的条件对象
Criteria criteria = Criteria.where("food").exists(true);
List<AggregationResults<Map>> ans = new ArrayList<>();
// 3.用for循环模拟三个分页请求
for (int pageNumber = 1; pageNumber <= 3; pageNumber++) {
Aggregation aggregation = Aggregation.newAggregation(
// 4.过滤阶段:传入构建好的条件对象
Aggregation.match(criteria),
// 5.分组阶段:
// 1:按food.grade分组
// 2:$$ROOT代表引用原文档,并放入pandas数组
Aggregation.group("food.grade").push("$$ROOT").as("pandas"),
// 6.拆分阶段:
// 将前面每个分组的pandas数组拆成一个个文档(index:下标,true:防丢失)
Aggregation.unwind("pandas", "index", true),
// 7.投影阶段:
// 去掉部分字段不显示,只显示_id(原food.grade)、name、age、index字段
Aggregation.project("_id", "pandas.name", "pandas.age", "index"),
// 8.分页阶段:使用skip、limit来区分读取不同页的数据
Aggregation.skip((long) (pageNumber - 1) * pageNumber),
Aggregation.limit(pageSize)
);
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "animals", Map.class);
ans.add(results);
}
return ans;
}
}
嵌套文档操作(update.push & update.pull)
// ========== 插入嵌套文档示例 ============
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import com.mongodb.client.model.Projections;
public class NestedDocumentService {
@Autowired
private MongoTemplate mongoTemplate;
public void insertNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedDocument) {
// 获取父文档
Query query = new Query(Criteria.where("_id").is(parentId));
query.fields().include("_id");
Object parentDocument = mongoTemplate.findOne(query, parentCollectionName);
// 确保父文档存在
if (parentDocument != null) {
// 创建嵌套文档的字段路径
String nestedFieldPath = nestedCollectionName + ".$";
// 创建更新对象,并使用push操作符插入嵌套文档
Update update = new Update();
update.push(nestedFieldPath, nestedDocument);
// 更新父文档
mongoTemplate.updateFirst(query, update, parentCollectionName);
}
}
}
// ========== 删除嵌套文档示例 ============
public void deleteNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedId) {
// 获取父文档
Query query = new Query(Criteria.where("_id").is(parentId));
query.fields().include("_id");
Object parentDocument = mongoTemplate.findOne(query, parentCollectionName);
// 确保父文档存在
if (parentDocument != null) {
// 创建嵌套文档的字段路径
String nestedFieldPath = nestedCollectionName + ".$";
// 创建更新对象,并使用pull操作符删除嵌套文档
Update update = new Update();
update.pull(nestedFieldPath, nestedId);
// 更新父文档
mongoTemplate.updateFirst(query, update, parentCollectionName);
}
}
}
对于嵌套文档的更新:
假设有一个名为users
的集合,其中的文档包含嵌套的address
字段,我们想要更新一个特定用户的地址。
@Autowired
private MongoTemplate mongoTemplate;
public void updateUserAddress(String userId, String street, String city) {
Query query = new Query(Criteria.where("_id").is(userId));
Update update = new Update();
// 使用点表示法访问嵌套文档的字段
update.set("address.street", street);
update.set("address.city", city);
// 更新第一个匹配的文档
mongoTemplate.updateFirst(query, update, "users");
}
九:ExampleMatcher匹配器:了解
package com.cui.springmongodemo.service;
import com.cui.springmongodemo.entity.Animals;
import java.util.List;
/**
* <p>
* 功能描述:ExampleMatcher相关操作介绍
* </p>
*
* @author cui haida
* @date 2023/11/26/7:32
*/
public interface ExampleMatcherService {
List<Animals> findAnimalsByName(Animals animals);
}
package com.cui.springmongodemo.service.impl;
import com.cui.springmongodemo.entity.Animals;
import com.cui.springmongodemo.service.ExampleMatcherService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 功能描述:ExampleMatcher匹配器测试
* --------- ExampleMatcher提供了三个方法用于创建匹配器:
* // 创建一个默认的ExampleMatcher实例(底层调用了matchingAll()方法)
* static ExampleMatcher matching();
* // 创建一个匹配所有属性(字段)的ExampleMatcher实例
* static ExampleMatcher matchingAll();
* // 创建一个匹配任意属性(字段)的ExampleMatcher实例
* static ExampleMatcher matchingAny();
*
* ---------- 自定义匹配规则的方法:
* // 为指定字段设置自定义的匹配规则
* ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
* // 为字符串设置自定义的匹配规则
* ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher);
* // 设置匹配时忽略大小写
* ExampleMatcher withIgnoreCase();
* // 设置匹配时包含空值字段
* ExampleMatcher withIncludeNullValues();
* // 设置匹配时忽略空值字段
* ExampleMatcher withIgnoreNullValues();
* // 为指定字段设置“字段值转换器”
* ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer);
* // 设置匹配时要排除的字段值
* ExampleMatcher withIgnorePaths(String... ignoredPaths);
*
* ------------ GenericPropertyMatcher类型,而该类中提供了一些内置规则方法
* // 设置匹配时,指定字段的值,必须包含给定值
* public GenericPropertyMatcher contains();
* // 设置匹配时,指定字段的值,必须以给定值开头
* public GenericPropertyMatcher startsWith();
* // 设置匹配时,指定字段的值,必须以给定值结尾
* public GenericPropertyMatcher endsWith();
* // 设置匹配时,指定字段的值,必须与给定值完全匹配
* public GenericPropertyMatcher exact();
* // 设置匹配时,指定字段的值,必须符合给定的正则表达式
* public GenericPropertyMatcher regex();
* // 设置匹配时,指定字段的值会区分大小写
* public GenericPropertyMatcher caseSensitive();
* // 设置匹配时,指定字段的值不区分大小写
* public GenericPropertyMatcher ignoreCase();
*
* </p>
*
* @author cui haida
* @date 2023/11/26/7:34
*/
@Service
@Slf4j
public class ExampleMatcherServiceImpl implements ExampleMatcherService {
@Resource
private MongoTemplate mongoTemplate;
@Override
public List<Animals> findAnimalsByName(Animals animals) {
// 创建了一个匹配器
ExampleMatcher matcher = ExampleMatcher.matching()
// 匹配规则一:忽略大小写
.withIgnoreCase()
// 匹配规则二:匹配字段:name字段,匹配关系:包含关系
.withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains());
// matcher + 参数 -> 创建出匹配器实例
Example<Animals> example = Example.of(animals, matcher);
// 将其传入到条件对象中
Criteria criteria = new Criteria().alike(example);
// mongoTemplate
return mongoTemplate.find(Query.query(criteria), Animals.class);
}
}
3:索引操作
一:创建索引(indexOps + ensureIndex)
// 在name字段创建一个唯一正序索引
Index index = new Index().on("name", Sort.Direction.ASC).unique();
// ensureIndex
mongoTemplate.indexOps("animals").ensureIndex(index);
二:查询索引(indexOps + getIndexInfo)
public List<IndexInfo> getIndexInfo(String collectionName) {
return mongoTemplate.indexOps(collectionName).getIndexInfo();
}
三:删除索引(indexOps + dropIndex)
// 删除指定的索引
mongoTemplate.getCollection(COLLECTION_NAME).dropIndex(indexName);
// 删除指定的索引方式2
mongoTemplate.indexOps(COLLECTION_NAME).dropIndex(indexName);
// 删除全部的索引
mongoTemplate.getCollection(COLLECTION_NAME).dropIndexes();
四:基础项目演示
项目结构如下
依赖导入和配置,实体类在上面已经给出。
1:repository介绍
package com.cui.mongo_demo.service;
import com.cui.mongo_demo.entity.model.Animal;
import org.springframework.data.domain.Page;
import java.util.List;
/**
* service层接口
* @author cui haida
* 2025/1/27
*/
public interface AnimalService {
/**
* 新增保存动物
* @param animal 动物对象
*/
void save(Animal animal);
/**
* 通过id删除
* @param id 要删除的对象的id
*/
void deleteById(Integer id);
/**
* 更新动物
* @param animal 要更新的动物
*/
void update(Animal animal);
/**
* 通过id返回指定的动物
* @param id 要查询的动物的id
* @return 查询到的动物对象
*/
Animal findById(Integer id);
/**
* 拿到集合中的所有的动物对象
* @return 所有的动物对象
*/
List<Animal> findAll();
/**
* 查询年龄小于指定岁数的前三条数据
* @param age 年龄
* @return 年龄小于指定岁数的前三条数据
*/
List<Animal> findTop3(int age);
/**
* 分页测试
* @param pageNumber 页码
* @param pageSize 页大小
* @return 分页结果
*/
Page<Animal> findByAgeNotNull(int pageNumber, int pageSize);
/**
* 自定义注解测试
* @param age 年龄
* @return 返回 < 输入年龄的动物集合
*/
List<Animal> queryByDefine(int age);
/**
* 事务测试
*/
void mongoTransaction();
}
package com.cui.mongo_demo.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.cui.mongo_demo.entity.model.Animal;
import com.cui.mongo_demo.repository.AnimalRepository;
import com.cui.mongo_demo.service.AnimalService;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
/**
* @author cui haida
* 2025/1/27
*/
@Service
public class AnimalServiceImpl implements AnimalService {
@Resource
private AnimalRepository animalRepository;
@Resource
private MongoClient mongoClient;
@Override
public void save(Animal animal) {
animalRepository.save(animal);
}
@Override
public void deleteById(Integer id) {
animalRepository.deleteById(id);
}
@Override
public void update(Animal animal) {
animalRepository.save(animal);
}
@Override
public Animal findById(Integer id) {
Optional<Animal> animal = animalRepository.findById(id);
return animal.orElse(null);
}
@Override
public List<Animal> findAll() {
return animalRepository.findAll();
}
@Override
public List<Animal> findTop3(int age) {
return animalRepository.findTop3ByAgeLessThan(age);
}
@Override
public Page<Animal> findByAgeNotNull(int pageNumber, int pageSize) {
PageRequest pageRequest = PageRequest.of(pageNumber, pageSize);
return animalRepository.findByAgeNotNull(pageRequest);
}
@Override
public List<Animal> queryByDefine(int age) {
return animalRepository.queryByDefine(age);
}
@Override
public void mongoTransaction() {
// 1.先通过mongoClient开启一个session会话
ClientSession session = mongoClient.startSession();
try{
// 2.通过session开启事务
session.startTransaction();
// 3.创建一个实体对象
Animal animals = new Animal();
animals.setId(222);
animals.setName("白白");
animals.setColor("白色");
animals.setAge(1);
// 4.通过mongoClient获取集合对象
MongoCollection<Document> collection = mongoClient.getDatabase("test").getCollection("animals");
// 5.通过集合对象提供的insert方法插入数据
collection.insertOne(
session,
Document.parse(JSONObject.toJSONString(animals))
);
// 6.模拟执行异常
int n = 100 / 0;
// 7.如果执行到这里,说明执行没报错,提交事务
session.commitTransaction();
} catch (Exception e) {
// 8.如果进入了catch,说明出现异常,回滚事务
session.abortTransaction();
e.printStackTrace();
}
// 9.关闭前面开启的session会话
session.close();
}
}
2:template介绍
package com.cui.mongo_demo.service;
import com.cui.mongo_demo.entity.model.Animal;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.index.IndexInfo;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author cui haida
* 2025/1/27
*/
public interface TemplateService {
/**
* 创建集合
*
* @param collectionName 集合名称
*/
public void createCollection(String collectionName);
/**
* 创建集合并设置固定大小
*
* @param collectionName 集合名称
* @param size 集合大小
* @param maxDocCount 最大集合文档数量
* @return 集合创建结果
*/
public Integer createCollectionFixedSize(String collectionName, Long size, Long maxDocCount);
/**
* 获取所有集合名称
*
* @return 集合名称列表
*/
public Set<String> getAllCollectionNames();
/**
* 判断集合是否存在
*
* @param collectionName 集合名称
* @return 集合是否存在
*/
public boolean existCollectionByName(String collectionName);
/**
* 删除集合
*
* @param collectionName 集合名称
*/
public void dropCollection(String collectionName);
/**
* 插入文档
*
* @param animal 文档
* @return 插入结果
*/
public Animal insertDocument(Animal animal);
/**
* 批量插入文档
*
* @param animals 文档列表
* @return 插入结果
*/
public List<Animal> batchInsertDocument(List<Animal> animals);
/**
* 保存文档
*
* @param animal 文档
* @return 保存结果
*/
public Animal saveAnimal(Animal animal);
/**
* 查询所有文档
*
* @return 文档列表
*/
public List<Animal> findAll();
/**
* 根据id查询
*
* @param id 文档id
* @return 文档
*/
public Animal findById(Integer id);
/**
* 根据条件查询
*
* @param sex 性别
* @param age 年龄
* @return 文档列表
*/
public List<Animal> findByCondition(String sex, Integer age);
/**
* 根据年龄更新
*
* @param oldAge 旧年龄
* @param newAge 新年龄
* @param newName 新名字
*/
public void updateByAge(Integer oldAge, Integer newAge, String newName);
/**
* 根据年龄和性别删除
*
* @param age 年龄
* @param sex 性别
*/
public void removeByAgeAndSex(Integer age, String sex);
/**
* 分页查询
*
* @param age 年龄
* @param pageNum 页码
* @param pageSize 页大小
* @return 文档列表
*/
public List<Animal> pageByAge(Integer age, Integer pageNum, Integer pageSize);
/**
* 聚合查询
*
* @return 聚合结果
*/
public AggregationResults<Map> aggByAgeAndCount();
/**
* 聚合查询
*
* @return 聚合结果
*/
public AggregationResults<Map> aggOpMinAndMax();
/**
* 聚合查询
*
* @return 聚合结果
*/
public AggregationResults<Map> aggOpComplex(int bottomAge);
/**
* 聚合查询
*
* @return 聚合结果
*/
public List<AggregationResults<Map>> aggOpComplexOfPage(int bottomAge);
/**
* 插入嵌套文档
*
* @param parentCollectionName 父集合名称
* @param parentId 父文档id
* @param nestedCollectionName 嵌套集合名称
* @param nestedDocument 嵌套文档
*/
public void insertNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedDocument);
/**
* 删除嵌套文档
*
* @param parentCollectionName 父集合名称
* @param parentId 父文档id
* @param nestedCollectionName 嵌套集合名称
* @param nestedId 嵌套文档id
*/
public void deleteNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedId);
/**
* 创建索引
* @param collectionName 集合名称
*/
public void createIndex(String collectionName, String fieldName);
/**
* 获取索引信息
* @param collectionName 集合名称
* @return 索引信息
*/
public List<IndexInfo> getIndexInfo(String collectionName);
/**
* 删除索引
* @param collectionName 集合名称
* @param fieldName 索引对应的字段的名称
*/
public void removeIndex(String collectionName, String fieldName);
/**
* 删除所有索引
* @param collectionName 集合名称
*/
public void removeAllIndex(String collectionName);
}
package com.cui.mongo_demo.service.impl;
import com.cui.mongo_demo.entity.model.Animal;
import com.cui.mongo_demo.service.TemplateService;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.CollectionOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* @author cui haida
* 2025/1/27
*/
@Service
public class TemplateServiceImpl implements TemplateService {
private final String COLLECTION_NAME = "animal";
private final MongoTemplate mongoTemplate;
public TemplateServiceImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
@Override
public void createCollection(String collectionName) {
mongoTemplate.createCollection(collectionName);
}
@Override
public Integer createCollectionFixedSize(String collectionName, Long size, Long maxDocCount) {
// .capped() // 创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档
// .size(size) // 固定集合指定一个最大值,以千字节计(KB),如果 capped 为 true,也需要指定该字段
// .maxDocuments(maxDocCount); // 指定固定集合中包含文档的最大数量
CollectionOptions collectionOptions = CollectionOptions.empty()
.capped()
.size(size)
.maxDocuments(maxDocCount);
mongoTemplate.createCollection(collectionName, collectionOptions);
return mongoTemplate.collectionExists(collectionName) ? 1 : 0;
}
@Override
public Set<String> getAllCollectionNames() {
return mongoTemplate.getCollectionNames();
}
@Override
public boolean existCollectionByName(String collectionName) {
return mongoTemplate.collectionExists(collectionName);
}
@Override
public void dropCollection(String collectionName) {
mongoTemplate.getCollection(collectionName).drop();
}
@Override
public Animal insertDocument(Animal animal) {
return mongoTemplate.insert(animal, COLLECTION_NAME);
}
@Override
public List<Animal> batchInsertDocument(List<Animal> animals) {
Collection<Animal> as = mongoTemplate.insert(animals, COLLECTION_NAME);
return new ArrayList<>(as);
}
@Override
public Animal saveAnimal(Animal animal) {
return mongoTemplate.save(animal, COLLECTION_NAME);
}
@Override
public List<Animal> findAll() {
return mongoTemplate.findAll(Animal.class, COLLECTION_NAME);
}
@Override
public Animal findById(Integer id) {
return mongoTemplate.findById(id, Animal.class, COLLECTION_NAME);
}
@Override
public List<Animal> findByCondition(String sex, Integer age) {
// 1:构建条件-准则
Criteria sexOfCriteria = Criteria.where("sex").is(sex);
Criteria ageOfCriteria = Criteria.where("age").is(age);
// 指定两个准则的拼接条件为and, 也就是 sex = ${sex} and age = ${age}
Criteria criteria = new Criteria().andOperator(sexOfCriteria, ageOfCriteria);
// 2:基于准则构建查询条件
Query query = new Query(criteria);
// 3:通过find方法查询数据,返回List查询结果
return mongoTemplate.find(query, Animal.class, COLLECTION_NAME);
}
@Override
public void updateByAge(Integer oldAge, Integer newAge, String newName) {
// 1:指定更新准则
Criteria criteria = Criteria.where("age").is(oldAge);
Query query = new Query(criteria);
// 2:指定更新内容
Update update = new Update();
update.set("age", newAge);
update.set("name", newName);
// 3:通过updateMulti方法更新数据, 还有updateFirst和upsert
mongoTemplate.updateMulti(query, update, Animal.class, COLLECTION_NAME);
}
@Override
public void removeByAgeAndSex(Integer age, String sex) {
// 1:指定删除条件
Criteria criteria = Criteria.where("age").is(age).and("sex").is(sex);
Query query = new Query(criteria);
mongoTemplate.remove(query, Animal.class, COLLECTION_NAME);
}
@Override
public List<Animal> pageByAge(Integer age, Integer pageNum, Integer pageSize) {
// 1:构建查询条件
Criteria criteria = Criteria.where("age").is(age);
Query query = new Query(criteria);
// 加入分页条件, 并通过年龄进行倒排(先进行倒排再分页)
query.with(PageRequest.of(pageNum, pageSize, Sort.Direction.DESC, "age"));
// 2:通过find方法查询数据,返回List查询结果
return mongoTemplate.find(query, Animal.class, COLLECTION_NAME);
}
@Override
public AggregationResults<Map> aggByAgeAndCount() {
// 1. 构建聚合信息
Aggregation aggregation = Aggregation.newAggregation(
// 基于年龄字段分组,接着统计每组数量,并为统计字段取别名
Aggregation.group("age")
.count().as("count"),
// 基于分组后的_id字段(原age)字段排序(升序)
Aggregation.sort(Sort.Direction.ASC, "_id")
);
// 2. 执行聚合操作,返回结果, 返回结果为Map类型
// 第一个参数是聚合条件,第二个参数是作用的集合名称,第三个参数是返回类型
return mongoTemplate.aggregate(aggregation, "animals", Map.class);
}
@Override
public AggregationResults<Map> aggOpMinAndMax() {
// 1. 构建聚合条件 Aggregation.newAggregation( condition1, condition2...)
Aggregation aggregation = Aggregation.newAggregation(
// 通过颜色进行分组,然后统计最大值、最小值、平均值。
Aggregation.group("color")
.max("age").as("max_age")
.min("age").as("min_age")
.avg("age").as("avg_age"),
// 排序,按照最大值进行倒排
Aggregation.sort(Sort.Direction.DESC, "max_age")
);
// 2. 执行聚合操作,返回结果
return mongoTemplate.aggregate(aggregation, "animals", Map.class);
}
@Override
public AggregationResults<Map> aggOpComplex(int bottomAge) {
// 1. 过滤掉food为空,以及age小于3岁的数据的条件
Criteria criteria =
Criteria.where("food").exists(true).and("age").gte(3);
// 2. 构建聚合条件
Aggregation aggregation = Aggregation.newAggregation(
// 1:match先进行过滤
Aggregation.match(criteria),
// 2:分组 + 聚合函数
Aggregation.group("food.grade")
// 通过food.grade进行分组,然后统计每一个分组的信息
.avg("age").as("avg_age")
.first("name").as("first_name")
.last("name").as("last_name")
.push("name").as("names"),
// 排序
Aggregation.sort(Sort.Direction.ASC, "age")
);
// 3. 执行聚合操作,返回结果
return mongoTemplate.aggregate(aggregation, "animals", Map.class);
}
@Override
public List<AggregationResults<Map>> aggOpComplexOfPage(int bottomAge) {
// 1.每页的数据量为2条
int pageSize = 2;
// 2.过滤掉food字段为空的条件对象
Criteria criteria = Criteria.where("food").exists(true);
// 初始化存储结果的数据结构
List<AggregationResults<Map>> ans = new ArrayList<>();
// 3.用for循环模拟分页查询
for (int pageNumber = 1; pageNumber <= 3; pageNumber++) {
Aggregation aggregation = Aggregation.newAggregation(
// 4.过滤阶段:传入构建好的条件对象
Aggregation.match(criteria),
// 5.分组阶段:
// 1:按food.grade分组
// 2:$$ROOT代表引用原文档,并放入pandas数组(// 将源文档信息推入(push...as)pandas数组)
Aggregation.group("food.grade")
.push("$$ROOT").as("pandas"),
// 6.拆分阶段:
// 将前面每个分组的pandas数组拆成一个个文档(index:下标,true:防丢失)
Aggregation.unwind("pandas", "index", true),
// 7.投影阶段:
// 去掉部分字段不显示,只显示_id(原food.grade)、name、age、index字段
Aggregation.project("_id", "pandas.name", "pandas.age", "index"),
// 8.分页阶段:使用skip、limit来区分读取不同页的数据
Aggregation.skip((long) (pageNumber - 1) * pageNumber),
Aggregation.limit(pageSize)
);
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "animals", Map.class);
ans.add(results);
}
return ans;
}
@Override
public void insertNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedDocument) {
// 1. 获取父文档
Query query = new Query(Criteria.where("_id").is(parentId));
query.fields().include("_id");
Animal parentDocument = mongoTemplate.findOne(query, Animal.class, parentCollectionName);
if (parentDocument != null) {
// 2.如果父文档存在,则插入嵌套文档
String nestedFieldPath = nestedCollectionName + ".$";
// 3.创建更新对象,并使用push操作符插入嵌套文档
Update update = new Update();
update.push(nestedFieldPath, nestedDocument);
// 4.更新父文档
mongoTemplate.updateFirst(query, update, parentCollectionName);
}
}
@Override
public void deleteNestedDocument(String parentCollectionName, String parentId, String nestedCollectionName, Object nestedId) {
// 1. 获取父文档
Query query = new Query(Criteria.where("_id").is(parentId));
query.fields().include("_id");
Animal parentDocument = mongoTemplate.findOne(query, Animal.class, parentCollectionName);
if (parentDocument != null) {
// 2.如果父文档存在,则删除嵌套文档
String nestedFieldPath = nestedCollectionName + ".$." + "_id";
// 3.创建更新对象,并使用pull操作符删除嵌套文档
Update update = new Update();
update.pull(nestedFieldPath, nestedId);
// 4.更新父文档
mongoTemplate.updateFirst(query, update, parentCollectionName);
}
}
@Override
public void createIndex(String collectionName, String fieldName) {
// 1.创建索引信息
Index index = new Index()
// 作用在指定字段上,排序方式为正序
.on(fieldName, Sort.Direction.ASC)
// 是唯一索引
.unique();
// 2.indexOps()方法用于获取集合的索引操作对象,然后调用ensureIndex()方法来创建索引
mongoTemplate.indexOps(collectionName).ensureIndex(index);
}
@Override
public List<IndexInfo> getIndexInfo(String collectionName) {
return mongoTemplate.indexOps(collectionName).getIndexInfo();
}
@Override
public void removeIndex(String collectionName, String fieldName) {
// 删除指定字段上的索引
mongoTemplate.indexOps(collectionName).dropIndex(fieldName);
}
@Override
public void removeAllIndex(String collectionName) {
mongoTemplate.indexOps(collectionName).dropAllIndexes();
}
}
3:ExampleMatcher介绍
package com.cui.mongo_demo.service;
import com.cui.mongo_demo.entity.model.Animal;
import java.util.List;
/**
* @author cuihaida
* 2025/1/28
*/
public interface ExampleMatcherService {
/**
* 根据名称查询
* @param animal 查询条件
* @return 查询结果
*/
List<Animal> findAnimalsByName(Animal animal);
}
package com.cui.mongo_demo.service.impl;
import com.cui.mongo_demo.entity.model.Animal;
import com.cui.mongo_demo.service.ExampleMatcherService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* * 功能描述:ExampleMatcher匹配器测试
* * --------- ExampleMatcher提供了三个方法用于创建匹配器:
* * // 创建一个默认的ExampleMatcher实例(底层调用了matchingAll()方法)
* * static ExampleMatcher matching();
* * // 创建一个匹配所有属性(字段)的ExampleMatcher实例
* * static ExampleMatcher matchingAll();
* * // 创建一个匹配任意属性(字段)的ExampleMatcher实例
* * static ExampleMatcher matchingAny();
* *
* * ---------- 自定义匹配规则的方法:
* * // 为指定字段设置自定义的匹配规则
* * ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
* * // 为字符串设置自定义的匹配规则
* * ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher);
* * // 设置匹配时忽略大小写
* * ExampleMatcher withIgnoreCase();
* * // 设置匹配时包含空值字段
* * ExampleMatcher withIncludeNullValues();
* * // 设置匹配时忽略空值字段
* * ExampleMatcher withIgnoreNullValues();
* * // 为指定字段设置“字段值转换器”
* * ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer);
* * // 设置匹配时要排除的字段值
* * ExampleMatcher withIgnorePaths(String... ignoredPaths);
* *
* * ------------ GenericPropertyMatcher类型,而该类中提供了一些内置规则方法
* * // 设置匹配时,指定字段的值,必须包含给定值
* * public GenericPropertyMatcher contains();
* * // 设置匹配时,指定字段的值,必须以给定值开头
* * public GenericPropertyMatcher startsWith();
* * // 设置匹配时,指定字段的值,必须以给定值结尾
* * public GenericPropertyMatcher endsWith();
* * // 设置匹配时,指定字段的值,必须与给定值完全匹配
* * public GenericPropertyMatcher exact();
* * // 设置匹配时,指定字段的值,必须符合给定的正则表达式
* * public GenericPropertyMatcher regex();
* * // 设置匹配时,指定字段的值会区分大小写
* * public GenericPropertyMatcher caseSensitive();
* * // 设置匹配时,指定字段的值不区分大小写
* * public GenericPropertyMatcher ignoreCase();
* </p>
* @author cui haida
* 2025/1/28
*/
@Service
@Slf4j
public class ExampleMatcherServiceImpl implements ExampleMatcherService {
private final MongoTemplate mongoTemplate;
public ExampleMatcherServiceImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
@Override
public List<Animal> findAnimalsByName(Animal animal) {
// 创建了一个匹配器
ExampleMatcher matcher = ExampleMatcher.matching()
// 匹配规则一:忽略大小写
.withIgnoreCase()
// 匹配规则二:匹配字段:name字段,匹配关系:包含关系
.withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains());
// matcher + 参数 -> 创建出匹配器实例
Example<Animal> example = Example.of(animal, matcher);
// 将其传入到条件对象中
Criteria criteria = new Criteria().alike(example);
// mongoTemplate
return mongoTemplate.find(Query.query(criteria), Animal.class);
}
}