添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

MongoTemplate使用指南

本文正在参加「技术专题19期 漫谈数据库技术」活动

此文通过代码片段较为详细的介绍了 MongoTemplate 的使用方法。下文中所有代码片段仅为 demo 示例, 如进一步使用需要根据自身业务稍作修改。

一、引入环境

maven引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

gradle引入

implementation('org.springframework.boot:spring-boot-starter-data-mongodb')

使用MongoTemplate进行开发

通过 @Resource@Autowired 注解装配 MongoTemplate

* MongoTemplate 使用样例 @Component public class MongoTemplateUsageSample { @Resource private MongoTemplate mongoTemplate;

二、MongoTemplate使用

1、Criteria的使用

* Criteria 使用样例 private void criteriaUsageSample() { // 精确查询 { "username" : "admin" } 初始化 criteria 实例的两种方法 Criteria criteria = Criteria.where("username").is("admin"); Criteria cri = new Criteria("username").is("admin"); // 不等于查询 { "username" : { $ne : "admin" } } Criteria ne = Criteria.where("username").ne("admin"); // 模糊查询 { "username" : /admin/ } Criteria regex = Criteria.where("username").regex("^.*" + "admin" + ".*$"); // and 查询 { "username" : "admin", "phoneNumber" : "10086" } Criteria and = criteria.and("phoneNumber").is("10086"); and = new Criteria().andOperator(Criteria.where("username").is("admin"), Criteria.where("phoneNumber").is("10086")); // or 查询 $or:[ { "username" : "admin" }, { "username" : "anonymous" } ] Criteria or = criteria.orOperator(Criteria.where("username").is("admin"), Criteria.where("username").is("anonymous")); // in 查询 { "username" : { $in: ["admin", "anonymous"] } } Criteria in = Criteria.where("username").in(Lists.newArrayList("admin", "anonymous")); // nin 查询 { "username" : { $nin: ["admin", "anonymous"] } } Criteria nin = Criteria.where("username").nin(Lists.newArrayList("admin", "anonymous")); // lt/lte 比较查询 // 小于等于 { "crtDateTime": { $lte: ISODate("2001-01-01T00:00:00.000+08:00") } } Criteria lte = Criteria.where("crtDateTime").lte(LocalDateTime.now()); // { "age": { $lte: 18 } } lte = Criteria.where("age").lte(18); // 小于 { "crtDateTime": {$lt: ISODate("2001-01-01T00:00:00.000+08:00") } } Criteria lt = Criteria.where("crtDateTime").lt(LocalDateTime.now()); // { "age": { $lt: 18 } } lt = Criteria.where("age").lt(18); // gt/gte 比较查询 // 大于等于 { "crtDateTime": {$gte: ISODate("2001-01-01T00:00:00.000+08:00") } } Criteria gte = Criteria.where("crtDateTime").gte(LocalDateTime.now()); // { "age": { $gte: 18 } } gte = Criteria.where("age").gte(18); // 大于 { "crtDateTime": {$gt: ISODate("2001-01-01T00:00:00.000+08:00") } } Criteria gt = Criteria.where("crtDateTime").gt(LocalDateTime.now()); // { "age": { $gt: 18 } } gt = Criteria.where("age").gt(18); // 查询内嵌文档 { "usernameList" : { $elemMatch: { "username" : "admin" } } } Criteria elemMatch = Criteria.where("usernameList").elemMatch(Criteria.where("username").is("admin")); // api 中无具体接口的, 使用 document 拼接语句查询 { $expr : { $ne : [ "$A", "$B" ] } } Criteria andDocumentStructureMatches = criteria.andDocumentStructureMatches(() -> new Document().append("$expr", new Document("$ne", List.of("$A", "$B"))));

2、查询操作

* 查询操作 * 根据查询条件查询 * public <T> List<T> find(Query query, Class<T> entityClass) {} * 根据查询条件查询返回一条记录 * public <T> <T> findOne(Query query, Class<T> entityClass) {} * 查询该collection所有记录 * public <T> List<T> findAll(Class<T> entityClass) {} private void query() { // 组装查询条件(参数 Criteria 的详细用法见 criteriaUsageSample()) Query query = new Query(Criteria.where("username").is("admin")); // 查询唯一一条满足条件的数据(如果满足条件的数据多于1条,会报错) UserInfo one = mongoTemplate.findOne(query, UserInfo.class); // 查询满足条件的数据列表 List<UserInfo> list = mongoTemplate.find(query, UserInfo.class); // 查询所有记录 List<UserInfo> all = mongoTemplate.findAll(UserInfo.class); // 根据 filed 去重查询 List<UserInfo> distinctList = mongoTemplate.findDistinct(query, "username", UserInfo.class, UserInfo.class); // 查询总数 long count = mongoTemplate.count(query, UserInfo.class);

3、插入操作

* 插入操作 * 新增一条记录 * public <T> T insert(T objectToSave) {} * 在collectionName中新增一条记录 * public <T> T insert(T objectToSave, String collectionName) {} * 保存一条记录 * public <T> T save(T objectToSave) {} private void insert() { mongoTemplate.insert(new UserInfo()); mongoTemplate.insert(new UserInfo(), "userInfo"); mongoTemplate.save(new UserInfo());

4、删除操作

* 删除操作 * 根据Object删除 * public DeleteResult remove(Object object) {} * 根据查询条件进行删除 * public DeleteResult remove(Query query, Class<?> entityClass) {} private void remove() { mongoTemplate.remove(new UserInfo()); DeleteResult deleteResult = mongoTemplate.remove(new UserInfo(), "userInfo"); // 是否执行成功 deleteResult.wasAcknowledged(); // 删除数量 deleteResult.getDeletedCount();

5、更新操作

* 更新操作 private void update() { // 原子操作 Update update = new Update(); update.inc("number", 1); // 批量更新 mongoTemplate.updateMulti(new Query(), update, UserInfo.class); // 更新第一条 mongoTemplate.updateFirst(new Query(), update, UserInfo.class); // 更新数据, 如果不存在就插入 UpdateResult updateResult = mongoTemplate.upsert(new Query(), update, UserInfo.class); // 是否执行成功 updateResult.wasAcknowledged(); // 匹配到的数量 updateResult.getMatchedCount(); // 更新数量 updateResult.getModifiedCount(); // 插入新数据的id BsonValue upsertedId = updateResult.getUpsertedId();

6、聚合操作

* 聚合操作 private void aggregate() { // 构造聚合操作列表 List<AggregationOperation> operations = Lists.newArrayList( // 匹配操作(参数 Criteria 的详细用法见 criteriaUsageSample()) Aggregation.match(new Criteria()), // 随机操作(随机取 n 条数据) Aggregation.sample(10), // 分组操作(GroupOperation 的详细用法见 groupOperationUsageSample()) Aggregation.group(), // 关联操作 Aggregation.lookup("targetCollectionName", "_id", "_id", "res"), // 拆分操作 Aggregation.unwind("res"), // 排序操作 Aggregation.sort(Sort.by("")), // 跳过操作 Aggregation.skip(100), // 限制操作 (skip + limit 组合可以应用在复杂聚合的分页查询上) Aggregation.limit(10), // 投射操作(projectionOperation 的详细用法见 projectionOperationUsageSample()) Aggregation.project() // 构造聚合函数 Aggregation aggregation = Aggregation.newAggregation(operations); // 聚合查询 AggregationResults<UserInfo> aggregate = mongoTemplate.aggregate(aggregation, UserInfo.class, UserInfo.class); // 获得聚合查询结果集 List<UserInfo> mappedResults = aggregate.getMappedResults(); // 获得聚合查询结果 UserInfo uniqueMappedResult = aggregate.getUniqueMappedResult(); // 如无具体类接收返回结果 可用 org.bson.Document 接收 AggregationResults<Document> docs = mongoTemplate.aggregate(aggregation, UserInfo.class, Document.class); List<Document> results = docs.getMappedResults(); for (Document result : results) { var param = Optional.ofNullable(result.get("username")).orElse(null);

7、分组操作样例

* GroupOperation 分组操作样例 private void groupOperationUsageSample() { Aggregation.group("age") // 取分组后 age 字段里的第一个值 存入 age 字段 .first("age").as("age") // 取分组后的数据总数 存入 count 字段 .count().as("count") // 将分组后姓名存入 usernameList 列表 .push("username").as("usernameList") // 将分组后姓名存入 usernameSet 集合, 去重 .addToSet("username").as("usernameSet") // 取分组后学分总和 .sum("score").as("scoreSum") // 取分组后学分平均数 .avg("score").as("scoreAvg") // 取分组后学分最大值 .max("score").as("scoreMax") // 取分组后学分最小值 .min("score").as("scoreMin") // 取最后一条数据 .last("$$ROOT").as("data");

8、投射操作样例

* ProjectionOperation 投射操作样例 private void projectionOperationUsageSample() { Aggregation.project() // 希望显示的字段 .andInclude("age", "username", "usernameList", "usernameSet", "data") // 不希望显示的字段 .andExclude("_id") // 拼接一个需要展示的字段的表达式 此表达式用法较多 具体可查 AggregationExpression 实现类 .and(AggregationExpression.from(MongoExpression.create(""))).as("andExpression") // 例: 投射一个 username-age 的值给 custom 字段 .and(StringOperators.Concat.valueOf("username").concat("-").concatValueOf("age")).as("custom");

9、demo 根据某字段分组求和示例

* demo * 根据某字段分组求和示例 * mongo shell: * db.demo.aggregate([ * { * $match: { * score: { * $gte: 60 * } * } * }, * { * $group: { * _id: "$score", * score: { * $first: "$score", * }, * count: { * $sum: 1 * }, * usernameList: { * $push: "$username" * } * } * }, * { * $sort: { * score: -1 * } * }, * { * $limit: 10 * } private void groupByScore() { // 查询分数大于等于60分的人 Criteria criteria = Criteria.where("score").gte(60); List<AggregationOperation> operations = Lists.newArrayList( // 匹配查询条件 Aggregation.match(criteria), // 对分数进行分组 Aggregation.group("score") .first("score").as("score") // 求和 .count().as("count") // 将用户姓名添加到列表中 .push("username").as("usernameList"), // 按照分数排倒序 Aggregation.sort(Sort.by(Sort.Order.desc("score"))), // 只显示前10的分数 Aggregation.limit(10) Aggregation aggregation = Aggregation.newAggregation(operations); // aggregation -> 聚合函数, UserInfo.class -> 待查询的源数据表, Document.class -> 根据聚合函数查询到的结果存入的 output 类型 // 这里如果有构造好的接收类, 可以用其替换 Document.class, 返回构造类的 List 即可 AggregationResults<Document> docs = mongoTemplate.aggregate(aggregation, UserInfo.class, Document.class); // 遍历查询结果 for (Document doc : docs.getMappedResults()) { // 分数 Integer score = Optional.ofNullable(doc.get("score")) .map(Object::toString).map(Integer::parseInt).orElse(null); // 总数 Integer count = Optional.ofNullable(doc.get("count")) .map(Object::toString).map(Integer::parseInt).orElse(null); // 用户姓名列表 List<String> usernameList = (List<String>) Optional.ofNullable(doc.get("usernameList")) .orElse(new ArrayList<String>());

10、demo 按日期 日/周/月/年 分组求和统计示例

* demo1 * 按日期 日/周/月/年 分组求和统计示例(demo 以日为例) * mongo shell: * db.demo.aggregate([ * { * $project: { * date: { * $dateToString: { * format: '%Y-%m-%d', * date: { * $add: ['$crtDateTime', 2880000] * } * } * } * } * }, * { * $group: { * _id: "$date", * date: { * $first: "$date", * }, * count: { * $sum: 1 * } * } * }, * { * $sort: { * date: 1 * } * } private void demo1() { // DAY("天","{$dateToString:{format:'%Y-%m-%d',date:{$add:{'$%s', 28800000}}}}"), // WEEK("周","{$dateToString:{format:'%Y-%U',date:{$add:{'$%s', 28800000}}}}"), // MONTH("月","{$dateToString:{format:'%Y-%m',date:{$add:{'$%s', 28800000}}}}"), // YEAR("年","{$dateToString:{format:'%Y',date:{$add:{'$%s', 28800000}}}}"), // 将 LocalDateTime 时间根据 format 表达式转换为字符串(yyyy-MM-dd) 用于分组 String time = "{$dateToString:{format:'%%Y-%%m-%%d',date:{$add:['$%s', 28800000]}}}"; // crtDateTime 为要分组的时间字段 String expression = String.format(time, "crtDateTime"); List<AggregationOperation> operations = Lists.newArrayList( // 将 LocalDateTime 转为字符串时间 Aggregation.project().andExpression(expression).as("date"), // 分组求和 Aggregation.group("date").first("date").as("date").count().as("count"), // 按时间正序显示 Aggregation.sort(Sort.Direction.ASC, "date") Aggregation aggregation = Aggregation.newAggregation(operations); // 这里如果有构造好的接收类, 可以用其替换 Document.class, 返回构造类的 List 即可 AggregationResults<Document> docs = mongoTemplate.aggregate(aggregation, UserInfo.class, Document.class); // 遍历查询结果 for (Document doc : docs.getMappedResults()) { // 日期 String date = Optional.ofNullable(doc.get("date")).map(Object::toString).orElse(null); // 总数 Integer count = Optional.ofNullable(doc.get("count")) .map(Object::toString).map(Integer::parseInt).orElse(null);

文中代码库表 model 示例:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class UserInfo {
         * 用户名
        private String username;
        private Integer age;
        private Integer score;
         * 创建时间
        private LocalDateTime crtDateTime;
    逃离月球表面
      
粉丝