Spring Data MongoDB的核心用法 附示例代码

 更新时间:2026年02月27日 10:03:40   作者:没有腰的嘟嘟嘟  
本文将系统梳理Spring Data MongoDB 的核心用法,涵盖实体类注解、增删改查、条件查询、分页排序等常见场景,并提供可直接复用的代码示例,感兴趣的朋友跟随小编一起看看吧

 在使用 Spring Data MongoDB 操作 MongoDB 时,掌握实体映射规则与常用 CRUD 方法是开发高效应用的基础。本文将系统梳理 Spring Data MongoDB 的核心用法,涵盖实体类注解、增删改查、条件查询、分页排序等常见场景,并提供可直接复用的代码示例。

一、实体类与集合映射规则

  • 集合命名规则
    • 若实体类未使用 @Document 注解,则默认生成的集合名称为首字母小写的驼峰格式,且区分大小写
    • 例如:实体类名为 UserDao → 对应集合名为 userDao
    • 使用 @Document(collection = "users") 可显式指定集合名。
  • 主键标识
    • 使用 @Id 标注主键字段。
    • 若字段名为 id 或 _id,可省略 @Id 注解(Spring Data 会自动识别)。
  • 字段映射
    • 当 Java 字段名与 MongoDB 中的字段名不一致时,使用 @Field("db_field_name") 进行映射。
User user = new User("张三", 25);
User saved = mongoTemplate.insert(user); // 返回带主键的对象

⚠️ 注意:使用 mongoTemplate.insert() 插入数据时,MongoDB 会自动添加 _class 字段,存储该文档对应的 Java 类全限定名(如 com.example.User),用于反序列化时类型还原。

二、新增操作

User user = new User("张三", 25);
User saved = mongoTemplate.insert(user); // 返回带主键的对象
  • 若集合不存在,会自动创建。
  • 自动注入 _class 字段。

三、修改操作

3.1 全量替换(慎用)

mongoTemplate.save(user); 

⚠️ 此方法会完全覆盖原文档!即使只设置了部分字段,其他字段也会被置为 null

3.2 局部更新(推荐)

Query query = Query.query(Criteria.where("id").is(userId));
Update update = new Update().set("name", "李四");
// 更新第一个匹配项
mongoTemplate.updateFirst(query, update, "userDao");
// 更新所有匹配项
mongoTemplate.updateMulti(query, update, "userDao");
  • Update.set() 对应 MongoDB 的 $set 操作符。
  • 第三个参数为集合名(非实体类名)。

四、删除操作

// 1. 根据主键删除(只需设置 id 字段)
User user = new User();
user.setId("60d..."); 
mongoTemplate.remove(user);
// 2. 根据条件删除
Query query = Query.query(Criteria.where("age").lt(18));
mongoTemplate.remove(query, "userDao");

五、查询操作

5.1 基础查询

// 查询全部
List<User> all = mongoTemplate.findAll(User.class);
// 根据 ID 查询
User user = mongoTemplate.findById("60d...", User.class);
// 查询第一条匹配结果
User first = mongoTemplate.findOne(query, User.class);
// 查询所有匹配结果
List<User> list = mongoTemplate.find(query, User.class);

5.2 条件查询(Criteria)

判断字段是否存在

// 等价于 db.user.find({name: {$exists: false}})
Query query = Query.query(Criteria.where("name").exists(false));

数值比较

含义MongoDB 操作符Spring Data 写法
大于$gtCriteria.where("age").gt(18)
大于等于$gteCriteria.where("score").gte(90)
小于$ltCriteria.where("price").lt(100)
小于等于$lteCriteria.where("quantity").lte(5)

组合示例:

Query query = new Query(Criteria.where("age").gt(20).lt(30));
List<User> users = mongoTemplate.find(query, User.class);

模糊查询(正则匹配)

// 包含匹配(任意位置)→ SQL: LIKE '%name%'
Query query = Query.query(Criteria.where("name").regex(Pattern.quote(name), "i"));
// 前缀匹配 → SQL: LIKE 'name%'
Query query = Query.query(Criteria.where("name").regex("^" + Pattern.quote(name), "i"));
// 后缀匹配 → SQL: LIKE '%name'
Query query = Query.query(Criteria.where("name").regex(Pattern.quote(name) + "$", "i"));

✅ 强烈建议使用 Pattern.quote() 转义用户输入,防止正则注入!

去重查询

Query query = Query.query(Criteria.where("name").regex("张"));
List<String> distinctNames = mongoTemplate.findDistinct(query, "name", User.class, String.class);

逻辑组合查询

  • AND 查询(姓名=张三 且 年龄>20):
Criteria criteria = Criteria.where("name").is("张三").and("age").gt(20);
Query query = Query.query(criteria);
  • OR 查询(姓名=张三 或 年龄>20):
Criteria criteria = new Criteria().orOperator(
    Criteria.where("name").is("张三"),
    Criteria.where("age").gt(20)
);
Query query = Query.query(criteria);
  • 混合 AND/OR((张三 & 18岁) OR (李四 & 20岁))
Criteria and1 = Criteria.where("name").is("张三").and("age").is(18);
Criteria and2 = Criteria.where("name").is("李四").and("age").is(20);
Criteria or = new Criteria().orOperator(and1, and2);
List<User> result = mongoTemplate.find(Query.query(or), User.class);

5.3 排序

Query query = new Query(Criteria.where("age").gte(2));
query.with(Sort.by(Sort.Direction.DESC, "age")); // 按 age 降序
List<User> list = mongoTemplate.find(query, User.class);

5.4 分页

// 第0页(第一页),每页2条
Pageable pageable = PageRequest.of(0, 2);
Query query = new Query().with(pageable);
List<User> pageData = mongoTemplate.find(query, User.class);
// 分页 + 排序
Pageable pageable = PageRequest.of(0, 2, Sort.by(Sort.Direction.ASC, "name"));

5.5 统计总数

TypedAggregation<User> agg = Aggregation.newAggregation(
    User.class,
    Aggregation.group().count().as("count")
);
AggregationResults<Map> result = mongoTemplate.aggregate(agg, Map.class);
Long total = (Long) result.getUniqueMappedResult().get("count");

5.6 带条件的分组统计

TypedAggregation<User> agg = Aggregation.newAggregation(
    User.class,
    Aggregation.match(Criteria.where("name").is("张三")), // 先过滤
    Aggregation.group("name").count().as("count")        // 再分组
);
List<Map> results = mongoTemplate.aggregate(agg, Map.class).getMappedResults();
for (Map map : results) {
    System.out.println("姓名: " + map.get("_id") + ", 数量: " + map.get("count"));
}

🔔 Aggregation.match() 放在 group 前表示“先过滤后分组”,放在后面则是“先分组后过滤”。

六、常见问题与解决方案(FAQ)

在实际使用 Spring Data MongoDB 过程中,开发者常会遇到一些“坑”。以下是几个高频问题及其解决方法:

1.插入数据后集合中多出_class字段

问题现象
使用 mongoTemplate.insert() 插入文档后,MongoDB 中自动多了一个 _class 字段,值为 Java 类的全限定名(如 com.example.User)。

原因
Spring Data MongoDB 默认启用类型映射(Type Mapping),用于反序列化时确定目标 Java 类型。

解决方案

  • 方案一(推荐):若不需要类型信息,可在实体类上添加 @Document 并禁用 _class
@Document(collection = "users")
@TypeAlias("user") // 可选:用简短别名替代全类名
public class User { ... }
  • 方案二(全局禁用):自定义 MongoCustomConversions,移除 _class 写入(适用于所有实体):
@Configuration
public class MongoConfig {
    @Bean
    public MongoCustomConversions mongoCustomConversions() {
        return new MongoCustomConversions(Collections.emptyList());
    }
}

⚠️ 注意:禁用后,若集合中存在多种类型文档,反序列化可能失败。

2.save()方法导致字段被置为 null

问题现象
调用 mongoTemplate.save(user) 更新对象时,未设置的字段在数据库中被清空为 null

原因
save() 是全量替换操作,不是局部更新。它会用传入对象的当前状态完全覆盖原文档。

解决方案
改用 updateFirst() 或 updateMulti() 做局部更新:

Query query = Query.query(Criteria.where("_id").is(userId));
Update update = new Update().set("name", "新名字").set("age", 30);
mongoTemplate.updateFirst(query, update, "users");

3.模糊查询因特殊字符报错或结果异常

问题现象
用户输入如 张*三李.四 等包含正则元字符的内容进行模糊查询时,抛出异常或匹配不到预期结果。

原因
regex() 直接将字符串当作正则表达式处理,而 *.? 等是正则特殊字符。

解决方案
使用 Pattern.quote() 对输入进行转义:

String keyword = "张*三";
String escapedKeyword = Pattern.quote(keyword); // 转义为字面量
Query query = Query.query(Criteria.where("name").regex(escapedKeyword, "i"));

✅ 同时建议加上 "i" 标志实现不区分大小写匹配。

4.分页查询总数不准或性能差

问题现象
分页时需要同时获取总条数,但每次都要执行一次 count 查询,大数据量下响应慢。

原因
PageRequest 本身不包含总数,需额外调用 mongoTemplate.count(query, clazz)

解决方案

  • 小数据量:直接 count。
  • 大数据量/高并发
    • 使用缓存(如 Redis 缓存总数,定时刷新)。
    • 改用“游标分页”(基于 _id 或时间戳的 gt/lt 查询),避免深度分页。
    • 示例(基于最后一条记录的 _id 下一页):
Query query = new Query(
    Criteria.where("_id").gt(lastId)
).limit(20).with(Sort.by(Sort.Direction.ASC, "_id"));

5.字段名不一致导致查不到数据

问题现象
Java 实体类字段为 userName,但数据库中是 user_name,查询返回 null 或空对象。

原因
未使用 @Field 注解建立映射关系。

解决方案
在实体类字段上显式标注:

public class User {
    @Field("user_name")
    private String userName;
    @Field("created_at")
    private Date createdAt;
}

6.主键类型不匹配(String vs ObjectId)

问题现象
实体类主键为 String id,但 MongoDB 自动生成的是 ObjectId,导致 findById() 查不到数据。

原因
MongoDB 默认 _id 为 ObjectId,而 Java 用 String 接收时格式不匹配。

解决方案

  • 统一使用 String(推荐):插入时手动指定 _id 为字符串,或让 Spring 自动转换。
  • 或使用 ObjectId 类型:
import org.bson.types.ObjectId;
@Id
private ObjectId id;
  • 若坚持用 String,确保插入和查询都使用相同格式(如 hex 字符串)。

7.聚合查询返回空或类型转换异常

问题现象
使用 aggregate() 后,getMappedResults() 返回空,或报 ClassCastException

原因
聚合结果结构与接收类型(如 Map.class)不匹配,或字段名错误(如 _id 被重命名)。

解决方案

  • 打印原始结果调试:
AggregationResults<Document> raw = mongoTemplate.aggregate(agg, Document.class);
System.out.println(raw.getRawResults());
  • 确保 as("xxx") 的字段名与接收对象一致。
  • 若只取一个值,可用 getUniqueMappedResult();多个结果用 getMappedResults()

到此这篇关于Spring Data MongoDB的核心用法 附示例代码的文章就介绍到这了,更多相关spring data mongodb用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot中的Logging详解

    SpringBoot中的Logging详解

    这篇文章主要介绍了SpringBoot中的Logging详解,log配置可能是被忽视的一个环节,一般的项目中日志配置好了基本上很少去改动,我们常规操作是log.info来记录日志内容,很少会有人注意到springBoot中日志的配置,需要的朋友可以参考下
    2023-09-09
  • Spring依赖注入和控制反转详情

    Spring依赖注入和控制反转详情

    这篇文章主要介绍了Spring依赖注入和控制反转详情,控制反转是面向对象编程中使用的术语,通过该术语,对象或对象集的控制权被赋予框架或由框架提供的容器。下文更多相关内容需要的小伙伴可以参考一下
    2022-05-05
  • Spring Boot项目中结合MyBatis实现MySQL的自动主从切换功能

    Spring Boot项目中结合MyBatis实现MySQL的自动主从切换功能

    这篇文章主要介绍了Spring Boot项目中结合MyBatis实现MySQL的自动主从切换功能,本文分步骤给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2025-04-04
  • spring cloud 配置中心客户端启动遇到的问题

    spring cloud 配置中心客户端启动遇到的问题

    这篇文章主要介绍了spring cloud 配置中心客户端启动遇到的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 深入解析Apache Kafka实时流处理平台

    深入解析Apache Kafka实时流处理平台

    这篇文章主要为大家介绍了Apache Kafka实时流处理平台深入解析,从基本概念到实战操作详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Springboot实现全局自定义异常的方法详解

    Springboot实现全局自定义异常的方法详解

    这篇文章主要介绍了Springboot实现全局自定义异常的方法详解,SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理,需要的朋友可以参考下
    2023-11-11
  • 解决IDEA 启动Tomcat控制台乱码问题

    解决IDEA 启动Tomcat控制台乱码问题

    今天在Idea中用Tomcat跑一个Web项目,启动后,Tomcat日志在控制台打印出来都是乱码,初次遇到这个问题真的很棘手,今天小编带领大家一起看看解决方法
    2018-07-07
  • Spring Boot中JSON数值溢出问题从报错到优雅解决办法

    Spring Boot中JSON数值溢出问题从报错到优雅解决办法

    这篇文章主要介绍了Spring Boot中JSON数值溢出问题从报错到优雅的解决办法,通过修改字段类型为Long、添加全局异常处理和数据校验,解决了该问题,文章还提供了类型范围推荐场景和常见问题解答,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04
  • Java基于socket编程相关知识解析

    Java基于socket编程相关知识解析

    这篇文章主要为大家详细解析了Java基于socket编程的相关知识,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • 新版Android Studio修改jdk版本的简单步骤

    新版Android Studio修改jdk版本的简单步骤

    这篇文章主要介绍了新版Android Studio修改jdk版本的简单步骤,升级Android Studio后,JDK版本设置选项消失,可以通过Settings面板进入Gradle设置,修改GradleJDK路径来解决问题,需要的朋友可以参考下
    2025-03-03

最新评论