java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > spring data mongodb用法

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

作者:没有腰的嘟嘟嘟

本文将系统梳理Spring Data MongoDB 的核心用法,涵盖实体类注解、增删改查、条件查询、分页排序等常见场景,并提供可直接复用的代码示例,感兴趣的朋友跟随小编一起看看吧

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

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

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); // 返回带主键的对象

三、修改操作

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");

四、删除操作

// 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);

逻辑组合查询

Criteria criteria = Criteria.where("name").is("张三").and("age").gt(20);
Query query = Query.query(criteria);
Criteria criteria = new Criteria().orOperator(
    Criteria.where("name").is("张三"),
    Criteria.where("age").gt(20)
);
Query query = Query.query(criteria);
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(collection = "users")
@TypeAlias("user") // 可选:用简短别名替代全类名
public class User { ... }
@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)

解决方案

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 接收时格式不匹配。

解决方案

import org.bson.types.ObjectId;
@Id
private ObjectId id;

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

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

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

解决方案

AggregationResults<Document> raw = mongoTemplate.aggregate(agg, Document.class);
System.out.println(raw.getRawResults());

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

您可能感兴趣的文章:
阅读全文