MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,且与关系数据库的最为相像的。它支持的数据结构非常松散,是类似 json 的 bson 格式,因此可以存储比较复杂的数据类型。Mongo 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
MongoDB 是一个文档数据库,它的数据以文档方式进行存储,将数据存储在类似 JSON 的 BSON 文档中,其特征如下:
简单介绍下 MongoDB 的概念知识,方便后续使用 SpringBoot 操作 MongoDB 时候对 MongoDB 相关概念知道其作用。
本文章并不是用于介绍 MongoDB 知识,而是介绍在 Java 语言中 SpringBoot 框架里如何操作 MongoDB。所以,在操作 MongoDB 前,最好对其知识点进行一下系统的学习。
MongoDB 基本概念指的是学习 MongoDB 最先应该了解的词汇,比如 MongoDB 中的"数据库"、"集合"、"文档"这三个名词:
当然,还有其它一些概念,比如:
以下为 MongoDB 中常用的几种数据类型:
这里使用 Spring Data MongoDB 封装的 MongoDB 官方 Java 驱动 MongoTemplate 对 MongoDB 进行操作。
关于使用简单的 Repositories 方式来操作 MongoDB 这种用法只能实现较简单的操作,使用简单但是灵活性比较差,所以这里就不介绍这种使用方式了。
Maven 引入 SpringBoot 和 MongoDB 相关依赖组件:
4.0.0 org.springframework.boot spring-boot-starter-parent2.3.0.RELEASE mydlq.club springboot-mongodb-template-example0.0.1 springboot-mongodb-template-example Demo project for Spring Boot MongoDB 1.8 org.springframework.boot spring-boot-starter-weborg.projectlombok lombokprovided org.springframework.boot spring-boot-starter-data-mongodborg.springframework.boot spring-boot-maven-plugin
依赖说明:
在 SpringBoot 的 application.yml 文件中添加连接 MongoDB 的配置参数,内容如下:
spring: data: mongodb: host: 127.0.0.1 port: 27017 database: test username: admin password: 123456
参数介绍:
创建用于示例中测试的实体 User 和 Status 类:
User.java
import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.ToString; import lombok.experimental.Accessors; import org.springframework.data.mongodb.core.mapping.MongoId; import java.util.Date; @Data @ToString @Accessors(chain = true) public class User { /** * 使用 @MongoID 能更清晰的指定 _id 主键 */ @MongoId private String id; private String name; private String sex; private Integer salary; private Integer age; @JsonFormat( pattern ="yyyy-MM-dd", timezone ="GMT+8") private Date birthday; private String remake; private Status status; }
使用 Lombok 中的 @Accessors(chain = true) 注解,能让我们方便使用链式方法创建实体对象。
Status.java
import lombok.Data; import lombok.ToString; import lombok.experimental.Accessors; @Data @ToString @Accessors(chain = true) public class Status { private Integer weight; private Integer height; }
创建 SpringBoot 启动类,方便测试:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnableSwagger2 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
(1)、创建集合
示例代码如下:
import org.springframework.data.mongodb.core.CollectionOptions; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class CreateCollectionService { @Resource private MongoTemplate mongoTemplate; /** * 创建【集合】 * * 创建一个大小没有限制的集合(默认集合创建方式) * * @return 创建集合的结果 */ public Object createCollection() { // 设置集合名称 String collectionName = "users1"; // 创建集合并返回集合信息 mongoTemplate.createCollection(collectionName); // 检测新的集合是否存在,返回创建结果 return mongoTemplate.collectionExists(collectionName) ? "创建视图成功" : "创建视图失败"; } /** * 创建【固定大小集合】 * * 创建集合并设置 `capped=true` 创建 `固定大小集合`,可以配置参数 `size` 限制文档大小,可以配置参数 `max` 限制集合文档数量。 * * @return 创建集合的结果 */ public Object createCollectionFixedSize() { // 设置集合名称 String collectionName = "users2"; // 设置集合参数 long size = 1024L; long max = 5L; // 创建固定大小集合 CollectionOptions collectionOptions = CollectionOptions.empty() // 创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 .capped() // 固定集合指定一个最大值,以千字节计(KB),如果 capped 为 true,也需要指定该字段。 .size(size) // 指定固定集合中包含文档的最大数量。 .maxDocuments(max); // 执行创建集合 mongoTemplate.createCollection(collectionName, collectionOptions); // 检测新的集合是否存在,返回创建结果 return mongoTemplate.collectionExists(collectionName) ? "创建视图成功" : "创建视图失败"; } /** * 创建【验证文档数据】的集合 * * 创建集合并在文档"插入"与"更新"时进行数据效验,如果符合创建集合设置的条件就进允许更新与插入,否则则按照设置的设置的策略进行处理。 * * * 效验级别: * - off:关闭数据校验。 * - strict:(默认值) 对所有的文档"插入"与"更新"操作有效。 * - moderate:仅对"插入"和满足校验规则的"文档"做"更新"操作有效。对已存在的不符合校验规则的"文档"无效。 * * 执行策略: * - error:(默认值) 文档必须满足校验规则,才能被写入。 * - warn:对于"文档"不符合校验规则的 MongoDB 允许写入,但会记录一条告警到 mongod.log 中去。日志内容记录报错信息以及该"文档"的完整记录。 * * @return 创建集合结果 */ public Object createCollectionValidation() { // 设置集合名称 String collectionName = "users3"; // 设置验证条件,只允许岁数大于20的用户信息插入 CriteriaDefinition criteria = Criteria.where("age").gt(20); // 设置集合选项验证对象 CollectionOptions collectionOptions = CollectionOptions.empty() .validator(Validator.criteria(criteria)) // 设置效验级别 .strictValidation() // 设置效验不通过后执行的动作 .failOnValidationError(); // 执行创建集合 mongoTemplate.createCollection(collectionName, collectionOptions); // 检测新的集合是否存在,返回创建结果 return mongoTemplate.collectionExists(collectionName) ? "创建集合成功" : "创建集合失败"; } }
(2)、查询集合
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class QueryCollectionService { @Resource private MongoTemplate mongoTemplate; /** * 获取【集合名称】列表 * * @return 集合名称列表 */ public Object getCollectionNames() { // 执行获取集合名称列表 return mongoTemplate.getCollectionNames(); } /** * 检测集合【是否存在】 * * @return 集合是否存在 */ public boolean collectionExists() { // 设置集合名称 String collectionName = "users"; // 检测新的集合是否存在,返回检测结果 return mongoTemplate.collectionExists(collectionName); } }
(3)、删除集合
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class RemoveCollectionService { @Resource private MongoTemplate mongoTemplate; /** * 删除【集合】 * * @return 创建集合结果 */ public Object dropCollection() { // 设置集合名称 String collectionName = "users3"; // 执行删除集合 mongoTemplate.getCollection(collectionName).drop(); // 检测新的集合是否存在,返回删除结果 return !mongoTemplate.collectionExists(collectionName) ? "删除集合成功" : "删除集合失败"; } }
import org.bson.Document; import org.bson.conversions.Bson; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; @Service public class ViewService { @Resource private MongoTemplate mongoTemplate; /** * 创建视图 * * @return 创建视图结果 */ public Object createView() { // 设置视图名 String newViewName = "usersView"; // 设置获取数据的集合名称 String collectionName = "users"; // 定义视图的管道,可是设置视图显示的内容多个筛选条件 Listpipeline = new ArrayList<>(); // 设置条件,用于筛选集合中的文档数据,只有符合条件的才会映射到视图中 pipeline.add(Document.parse("{\"$match\":{\"sex\":\"女\"}}")); // 执行创建视图 mongoTemplate.getDb().createView(newViewName, collectionName, pipeline); // 检测新的集合是否存在,返回创建结果 return mongoTemplate.collectionExists(newViewName) ? "创建视图成功" : "创建视图失败"; } /** * 删除视图 * * @return 删除视图结果 */ public Object dropView() { // 设置待删除的视图名称 String viewName = "usersView"; // 检测视图是否存在 if (mongoTemplate.collectionExists(viewName)) { // 删除视图 mongoTemplate.getDb().getCollection(viewName).drop(); return "删除视图成功"; } // 检测新的集合是否存在,返回创建结果 return !mongoTemplate.collectionExists(viewName) ? "删除视图成功" : "删除视图失败"; } }
(1)、文档插入
import lombok.extern.slf4j.Slf4j; import mydlq.club.example.entity.Status; import mydlq.club.example.entity.User; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; @Slf4j @Service public class InsertService { /** 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 插入【一条】文档数据,如果文档信息已经【存在就抛出异常】 * * @return 插入的文档信息 */ public Object insert() { // 设置用户信息 User user = new User() .setId("10") .setAge(22) .setSex("男") .setRemake("无") .setSalary(1500) .setName("zhangsan") .setBirthday(new Date()) .setStatus(new Status().setHeight(180).setWeight(150)); // 插入一条用户数据,如果文档信息已经存在就抛出异常 User newUser = mongoTemplate.insert(user, COLLECTION_NAME); // 输出存储结果 log.info("存储的用户信息为:{}", newUser); return newUser; } /** * 插入【多条】文档数据,如果文档信息已经【存在就抛出异常】 * * @return 插入的多个文档信息 * */ public Object insertMany(){ // 设置两个用户信息 User user1 = new User() .setId("11") .setAge(22) .setSex("男") .setRemake("无") .setSalary(1500) .setName("shiyi") .setBirthday(new Date()) .setStatus(new Status().setHeight(180).setWeight(150)); User user2 = new User() .setId("12") .setAge(22) .setSex("男") .setRemake("无") .setSalary(1500) .setName("shier") .setBirthday(new Date()) .setStatus(new Status().setHeight(180).setWeight(150)); // 使用户信息加入结合 ListuserList = new ArrayList<>(); userList.add(user1); userList.add(user2); // 插入一条用户数据,如果某个文档信息已经存在就抛出异常 Collection newUserList = mongoTemplate.insert(userList, COLLECTION_NAME); // 输出存储结果 for (User user : newUserList) { log.info("存储的用户信息为:{}", user); } return newUserList; } }
(2)、文档存储
import lombok.extern.slf4j.Slf4j; import mydlq.club.example.entity.Status; import mydlq.club.example.entity.User; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Date; @Slf4j @Service public class SaveService { /** 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 存储【一条】用户信息,如果文档信息已经【存在就执行更新】 * * @return 存储的文档信息 */ public Object save() { // 设置用户信息 User user = new User() .setId("13") .setAge(22) .setSex("男") .setRemake("无") .setSalary(2800) .setName("kuiba") .setBirthday(new Date()) .setStatus(new Status().setHeight(169).setWeight(150)); // 存储用户信息,如果文档信息已经存在就执行更新 User newUser = mongoTemplate.save(user, COLLECTION_NAME); // 输出存储结果 log.info("存储的用户信息为:{}", newUser); return newUser; } }
(3)、文档查询
import lombok.extern.slf4j.Slf4j; import mydlq.club.example.entity.User; import org.springframework.data.domain.Sort; 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.Arrays; import java.util.List; @Slf4j @Service public class QueryService { /** * 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 查询集合中的【全部】文档数据 * * @return 全部文档列表 */ public Object findAll() { // 执行查询集合中全部文档信息 ListdocumentList = mongoTemplate.findAll(User.class, COLLECTION_NAME); // 输出结果 for (User user : documentList) { log.info("用户信息:{}", user); } return documentList; } /** * 根据【文档ID】查询集合中文档数据 * * @return 文档信息 */ public Object findById() { // 设置查询的文档 ID String id = "1"; // 根据文档ID查询集合中文档数据,并转换为对应 Java 对象 User user = mongoTemplate.findById(id, User.class, COLLECTION_NAME); // 输出结果 log.info("用户信息:{}", user); return user; } /** * 根据【条件】查询集合中【符合条件】的文档,只取【第一条】数据 * * @return 符合条件的第一条文档 */ public Object findOne() { // 设置查询条件参数 int age = 22; // 创建条件对象 Criteria criteria = Criteria.where("age").is(age); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 查询一条文档,如果查询结果中有多条文档,那么就取第一条 User user = mongoTemplate.findOne(query, User.class, COLLECTION_NAME); // 输出结果 log.info("用户信息:{}", user); return user; } /** * 根据【条件】查询集合中【符合条件】的文档,获取其【文档列表】 * * @return 符合条件的文档列表 */ public Object findByCondition() { // 设置查询条件参数 String sex = "女"; // 创建条件对象 Criteria criteria = Criteria.where("sex").is(sex); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 查询并返回结果 List documentList = mongoTemplate.find(query, User.class, COLLECTION_NAME); // 输出结果 for (User user : documentList) { log.info("用户信息:{}", user); } return documentList; } /** * 根据【条件】查询集合中【符合条件】的文档,获取其【文档列表】并【排序】 * * @return 符合条件的文档列表 */ public Object findByConditionAndSort() { // 设置查询条件参数 String sex = "男"; String sort = "age"; // 创建条件对象 Criteria criteria = Criteria.where("sex").is(sex); // 创建查询对象,然后将条件对象添加到其中,然后根据指定字段进行排序 Query query = new Query(criteria).with(Sort.by(sort)); // 执行查询 List documentList = mongoTemplate.find(query, User.class, COLLECTION_NAME); // 输出结果 for (User user : documentList) { log.info("用户信息:{}", user); } return documentList; } /** * 根据【单个条件】查询集合中的文档数据,并【按指定字段进行排序】与【限制指定数目】 * * @return 符合条件的文档列表 */ public Object findByConditionAndSortLimit() { // 设置查询条件参数 String sex = "男"; String sort = "age"; int limit = 2; // 创建条件对象 Criteria criteria = Criteria.where("sex").is(sex); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria).with(Sort.by(sort)).limit(limit); // 执行查询 List documentList = mongoTemplate.find(query, User.class, COLLECTION_NAME); // 输出结果 for (User user : documentList) { log.info("用户信息:{}", user); } return documentList; } /** * 根据【单个条件】查询集合中的文档数据,并【按指定字段进行排序】与【并跳过指定数目】 * * @return 符合条件的文档列表 */ public Object findByConditionAndSortSkip() { // 设置查询条件参数 String sex = "男"; String sort = "age"; int skip = 1; // 创建条件对象 Criteria criteria = Criteria.where("sex").is(sex); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria).with(Sort.by(sort)).skip(skip); // 查询并返回结果 List documentList = mongoTemplate.find(query, User.class, COLLECTION_NAME); // 输出结果 for (User user : documentList) { log.info("用户信息:{}", user); } return documentList; } /** * 查询【存在指定字段名称】的文档数据 * * @return 符合条件的文档列表 */ public Object findByExistsField() { // 设置查询条件参数 String field = "sex"; // 创建条件 Criteria criteria = Criteria.where(field).exists(true); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 查询并返回结果 List documentList = mongoTemplate.find(query, User.class, COLLECTION_NAME); // 输出结果 for (User user : documentList) { log.info("用户信息:{}", user); } return documentList; } /** * 根据【AND】关联多个查询条件,查询集合中的文档数据 * * @return 符合条件的文档列表 */ public Object findByAndCondition() { // 设置查询条件参数 String sex = "男"; Integer age = 22; // 创建条件 Criteria criteriaSex = Criteria.where("sex").is(sex); Criteria criteriaAge = Criteria.where("age").is(age); // 创建条件对象,将上面条件进行 AND 关联 Criteria criteria = new Criteria().andOperator(criteriaSex, criteriaAge); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 查询并返回结果 List documentList = mongoTemplate.find(query, User.class, COLLECTION_NAME);
(4)、文档更新
import com.mongodb.client.result.UpdateResult; import lombok.extern.slf4j.Slf4j; import mydlq.club.example.entity.User; import org.springframework.data.domain.Sort; 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 org.springframework.stereotype.Service; import javax.annotation.Resource; @Slf4j @Service public class UpdateService { /** * 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 更新集合中【匹配】查询到的第一条文档数据,如果没有找到就【创建并插入一个新文档】 * * @return 执行更新的结果 */ public Object update() { // 创建条件对象 Criteria criteria = Criteria.where("age").is(30); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 创建更新对象,并设置更新的内容 Update update = new Update().set("age", 33).set("name", "zhangsansan"); // 执行更新,如果没有找到匹配查询的文档,则创建并插入一个新文档 UpdateResult result = mongoTemplate.upsert(query, update, User.class, COLLECTION_NAME); // 输出结果信息 String resultInfo = "匹配到" + result.getMatchedCount() + "条数据,对第一条数据进行了更改"; log.info("更新结果:{}", resultInfo); return resultInfo; } /** * 更新集合中【匹配】查询到的【文档数据集合】中的【第一条数据】 * * @return 执行更新的结果 */ public Object updateFirst() { // 创建条件对象 Criteria criteria = Criteria.where("name").is("zhangsan"); // 创建查询对象,然后将条件对象添加到其中,并设置排序 Query query = new Query(criteria).with(Sort.by("age").ascending()); // 创建更新对象,并设置更新的内容 Update update = new Update().set("age", 30).set("name", "zhangsansan"); // 执行更新 UpdateResult result = mongoTemplate.updateFirst(query, update, User.class, COLLECTION_NAME); // 输出结果信息 String resultInfo = "共匹配到" + result.getMatchedCount() + "条数据,修改了" + result.getModifiedCount() + "条数据"; log.info("更新结果:{}", resultInfo); return resultInfo; } /** * 更新【匹配查询】到的【文档数据集合】中的【所有数据】 * * @return 执行更新的结果 */ public Object updateMany() { // 创建条件对象 Criteria criteria = Criteria.where("age").gt(28); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 设置更新字段和更新的内容 Update update = new Update().set("age", 29).set("salary", "1999"); // 执行更新 UpdateResult result = mongoTemplate.updateMulti(query, update, User.class, COLLECTION_NAME); // 输出结果信息 String resultInfo = "总共匹配到" + result.getMatchedCount() + "条数据,修改了" + result.getModifiedCount() + "条数据"; log.info("更新结果:{}", resultInfo); return resultInfo; } }
(5)、文档删除
import com.mongodb.client.result.DeleteResult; import lombok.extern.slf4j.Slf4j; import mydlq.club.example.entity.User; 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; @Slf4j @Service public class RemoveService { /** * 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 删除集合中【符合条件】的【一个]或[多个】文档 * * @return 删除用户信息的结果 */ public Object remove() { // 设置查询条件参数 int age = 30; String sex = "男"; // 创建条件对象 Criteria criteria = Criteria.where("age").is(age).and("sex").is(sex); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 执行删除查找到的匹配的全部文档信息 DeleteResult result = mongoTemplate.remove(query, COLLECTION_NAME); // 输出结果信息 String resultInfo = "成功删除 " + result.getDeletedCount() + " 条文档信息"; log.info(resultInfo); return resultInfo; } /** * 删除【符合条件】的【单个文档】,并返回删除的文档。 * * @return 删除的用户信息 */ public Object findAndRemove() { // 设置查询条件参数 String name = "zhangsansan"; // 创建条件对象 Criteria criteria = Criteria.where("name").is(name); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 执行删除查找到的匹配的第一条文档,并返回删除的文档信息 User result = mongoTemplate.findAndRemove(query, User.class, COLLECTION_NAME); // 输出结果信息 String resultInfo = "成功删除文档信息,文档内容为:" + result; log.info(resultInfo); return result; } /** * 删除【符合条件】的【全部文档】,并返回删除的文档。 * * @return 删除的全部用户信息 */ public Object findAllAndRemove() { // 设置查询条件参数 int age = 22; // 创建条件对象 Criteria criteria = Criteria.where("age").is(age); // 创建查询对象,然后将条件对象添加到其中 Query query = new Query(criteria); // 执行删除查找到的匹配的全部文档,并返回删除的全部文档信息 ListresultList = mongoTemplate.findAllAndRemove(query, User.class, COLLECTION_NAME); // 输出结果信息 String resultInfo = "成功删除文档信息,文档内容为:" + resultList; log.info(resultInfo); return resultList; } }
(1)、聚合表达式
import lombok.extern.slf4j.Slf4j; 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.AggregationOperation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Map; /** * 聚合表达式 $group * * @author mydlq */ @Slf4j @Service public class AggregateGroupService { /** * 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 使用管道操作符 $group 结合 $count 方法进行聚合统计 * * @return 聚合结果 */ public Object aggregationGroupCount() { // 使用管道操作符 $group 进行分组,然后统计各个组的文档数量 AggregationOperation group = Aggregation.group("age").count().as("numCount"); // 将操作加入到聚合对象中 Aggregation aggregation = Aggregation.newAggregation(group); // 执行聚合查询 AggregationResults
(2)、聚合管道操作符
import lombok.extern.slf4j.Slf4j; 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.AggregationOperation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Map; @Slf4j @Service public class AggregatePipelineService { /** * 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 使用 $group 和 $match 聚合,先使用 $match 过滤文档,然后再使用 $group 进行分组 * * @return 聚合结果 */ public Object aggregateGroupMatch() { // 设置聚合条件,先使用 $match 过滤岁数大于 25 的用户,然后按性别分组,统计每组用户工资最高值 AggregationOperation match = Aggregation.match(Criteria.where("age").lt(25)); AggregationOperation group = Aggregation.group("sex").max("salary").as("sexSalary"); // 将操作加入到聚合对象中 Aggregation aggregation = Aggregation.newAggregation(match, group); // 执行聚合查询 AggregationResultsresults = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class); for (Map result : results.getMappedResults()) { log.info("{}", result); } return results.getMappedResults(); } /** * 使用 $group 和 $sort 聚合,先使用 $group 进行分组,然后再使用 $sort 排序 * * @return 聚合结果 */ public Object aggregateGroupSort() { // 设置聚合条件,按岁数分组,然后统计每组用户工资最大值和用户数,按每组用户工资最大值升序排序 AggregationOperation group = Aggregation.group("age") .max("salary").as("ageSalary") .count().as("ageCount"); AggregationOperation sort = Aggregation.sort(Sort.by("ageSalary").ascending()); // 将操作加入到聚合对象中 Aggregation aggregation = Aggregation.newAggregation(group, sort); // 执行聚合查询 AggregationResults results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class); for (Map result : results.getMappedResults()) { log.info("{}", result); } return results.getMappedResults(); } /** * 使用 $group 和 $limit 聚合,先使用 $group 进行分组,然后再使用 $limit 限制一定数目文档 * * @return 聚合结果 */ public Object aggregateGroupLimit() { // 设置聚合条件,先按岁数分组,然后求每组用户的工资总数、最大值、最小值、平均值,限制只能显示五条 AggregationOperation group = Aggregation.group("age") .sum("salary").as("sumSalary") .max("salary").as("maxSalary") .min("salary").as("minSalary") .avg("salary").as("avgSalary"); AggregationOperation limit = Aggregation.limit(5L); // 将操作加入到聚合对象中 Aggregation aggregation = Aggregation.newAggregation(group, limit); // 执行聚合查询 AggregationResults results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class); for (Map result : results.getMappedResults()) { log.info("{}", result); } return results.getMappedResults(); } /** * 使用 $group 和 $skip 聚合,先使用 $group 进行分组,然后再使用 $skip 跳过一定数目文档 * * @return 聚合结果 */ public Object aggregateGroupSkip() { // 设置聚合条件,先按岁数分组,然后求每组用户的工资总数、最大值、最小值、平均值,跳过前 2 条 AggregationOperation group = Aggregation.group("age") .sum("salary").as("sumSalary") .max("salary").as("maxSalary") .min("salary").as("minSalary") .avg("salary").as("avgSalary"); AggregationOperation limit = Aggregation.skip(2L); // 将操作加入到聚合对象中 Aggregation aggregation = Aggregation.newAggregation(group, limit); // 执行聚合查询 AggregationResults results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class); for (Map result : results.getMappedResults()) { log.info("{}", result); } return results.getMappedResults(); } /** * 使用 $group 和 $project 聚合,先使用 $group 进行分组,然后再使用 $project 限制显示的字段 * * @return 聚合结果 */ public Object aggregateGroupProject() { // 设置聚合条件,按岁数分组,然后求每组用户工资最大值、最小值,然后使用 $project 限制值显示 salaryMax 字段 AggregationOperation group = Aggregation.group("age") .max("salary").as("maxSalary") .min("salary").as("minSalary"); AggregationOperation project = Aggregation.project("maxSalary"); // 将操作加入到聚合对象中 Aggregation aggregation = Aggregation.newAggregation(group, project); // 执行聚合查询 AggregationResults results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class); for (Map result : results.getMappedResults()) { log.info("{}", result); } return results.getMappedResults(); } /** * 使用 $group 和 $unwind 聚合,先使用 $project 进行分组,然后再使用 $unwind 拆分文档中的数组为一条新文档记录 * * @return 聚合结果 */ public Object aggregateProjectUnwind() { // 设置聚合条件,设置显示`name`、`age`、`title`字段,然后将结果中的多条文档按 title 字段进行拆分 AggregationOperation project = Aggregation.project("name", "age", "title"); AggregationOperation unwind = Aggregation.unwind("title"); // 将操作加入到聚合对象中 Aggregation aggregation = Aggregation.newAggregation(project, unwind); // 执行聚合查询 AggregationResults results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class); for (Map result : results.getMappedResults()) { log.info("{}", result); } return results.getMappedResults(); } }
聚合管道操作符:
(1)、创建索引
import com.mongodb.client.model.Filters; import com.mongodb.client.model.IndexOptions; import com.mongodb.client.model.Indexes; import lombok.extern.slf4j.Slf4j; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Slf4j @Service public class CreateIndexService { /** 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 创建升序索引 * * @return 索引信息 */ public Object createAscendingIndex() { // 设置字段名称 String field = "name"; // 创建索引 return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.ascending(field)); } /** * 创建降序索引 * * @return 索引信息 */ public Object createDescendingIndex() { // 设置字段名称 String field = "name"; // 创建索引 return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.descending(field)); } /** * 创建升序复合索引 * * @return 索引信息 */ public Object createCompositeIndex() { // 设置字段名称 String field1 = "name"; String field2 = "age"; // 创建索引 return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.ascending(field1, field2)); } /** * 创建文字索引 * * @return 索引信息 */ public Object createTextIndex() { // 设置字段名称 String field = "name"; // 创建索引 return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.text(field)); } /** * 创建哈希索引 * * @return 索引信息 */ public Object createHashIndex() { // 设置字段名称 String field = "name"; // 创建索引 return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.hashed(field)); } /** * 创建升序唯一索引 * * @return 索引信息 */ public Object createUniqueIndex() { // 设置字段名称 String indexName = "name"; // 配置索引选项 IndexOptions options = new IndexOptions(); // 设置为唯一索引 options.unique(true); // 创建索引 return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.ascending(indexName), options); } /** * 创建局部索引 * * @return 索引信息 */ public Object createPartialIndex() { // 设置字段名称 String field = "name"; // 配置索引选项 IndexOptions options = new IndexOptions(); // 设置过滤条件 options.partialFilterExpression(Filters.exists("name", true)); // 创建索引 return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.ascending(field), options); } }
(2)、查询索引
import com.mongodb.client.ListIndexesIterable; import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * 查询索引操作 * * @author mydlq */ @Slf4j @Service public class QueryIndexService { /** 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; /** * 获取当前【集合】对应的【所有索引】的【名称列表】 * * @return 当前【集合】所有【索引名称列表】 */ public Object getIndexAll() { // 获取集合中所有列表 ListIndexesIterableindexList = mongoTemplate.getCollection(COLLECTION_NAME).listIndexes(); // 创建字符串集合 List list = new ArrayList<>(); // 获取集合中全部索引信息 for (Document document : indexList) { log.info("索引列表:{}",document); list.add(document); } return list; } }
(3)、删除索引
import lombok.extern.slf4j.Slf4j; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Slf4j @Service public class RemoveIndexService { @Resource private MongoTemplate mongoTemplate; /** 设置集合名称 */ private static final String COLLECTION_NAME = "users"; /** * 根据索引名称移除索引 */ public void removeIndex() { // 设置索引名称 String indexName = "name_1"; // 删除集合中某个索引 mongoTemplate.getCollection(COLLECTION_NAME).dropIndex(indexName); } /** * 移除全部索引 */ public void removeIndexAll() { // 删除集合中全部索引 mongoTemplate.getCollection(COLLECTION_NAME).dropIndexes(); } }
(1)、RunCommand 命令
import org.bson.Document; import org.bson.conversions.Bson; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class RunCommandService { @Resource private MongoTemplate mongoTemplate; /** * 执行 mongoDB 自定义命令,详情可以查看:https://docs.mongodb.com/manual/reference/command/ * * @return 执行命令返回结果的 Json 结果 * @description 执行自定义 mongoDB 命令 */ public Object runCommand() { // 自定义命令 String jsonCommand = "{\"buildInfo\":1}"; // 将 JSON 字符串解析成 MongoDB 命令 Bson bson = Document.parse(jsonCommand); // 执行自定义命令 return mongoTemplate.getDb().runCommand(bson); } }
注意:单节点 mongodb 不支持事务,需要搭建 MongoDB 复制集。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.MongoTransactionManager; /** * 配置事务管理器 * * @author mydlq */ @Configuration public class TransactionConfig { @Bean MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) { return new MongoTransactionManager(dbFactory); } }
import mydlq.club.example.entity.Status; import mydlq.club.example.entity.User; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Date; @Service public class TransactionExample { /** 设置集合名称 */ private static final String COLLECTION_NAME = "users"; @Resource private MongoTemplate mongoTemplate; @Transactional(rollbackFor = Exception.class) public Object transactionTest(){ // 设置两个用户信息 User user1 = new User() .setId("11") .setAge(22) .setSex("男") .setRemake("无") .setSalary(1500) .setName("shiyi") .setBirthday(new Date()) .setStatus(new Status().setHeight(180).setWeight(150)); // 插入数据 User newUser1 = mongoTemplate.insert(user1, COLLECTION_NAME); // 抛出异常,观察数据是否进行回滚 int error = 1/0; return newUser1; } }