1. 概述
本文将深入探讨 Spring Data MongoDB 的三大核心功能:索引管理、常用注解使用以及自定义转换器(Converter)。我们将基于 Spring Boot 环境进行演示,依赖会自动引入,无需手动添加。
这些内容在实际项目中非常实用,尤其在性能优化和数据映射定制方面,掌握它们能帮你少踩不少坑。✅
2. 索引(Indexes)
MongoDB 的查询性能高度依赖索引。Spring Data MongoDB 提供了多种方式来定义和管理索引。
2.1 @Indexed
注解
使用 @Indexed
可以标记某个字段需要创建索引:
@QueryEntity
@Document
public class User {
@Indexed
private String name;
// 其他字段...
}
但你会发现,即使加了注解,MongoDB 中并没有自动创建对应索引 ❌
db.user.getIndexes();
输出结果:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.user"
}
]
⚠️ 原因:从 Spring Data MongoDB 3.0 开始,自动索引创建默认是关闭的!
要开启,需要在配置类中重写 autoIndexCreation()
方法:
public class MongoConfig extends AbstractMongoClientConfiguration {
@Override
protected boolean autoIndexCreation() {
return true;
}
}
或者更简单粗暴的方式,在 application.yml
中直接开启:
spring:
data:
mongodb:
auto-index-creation: true
再次查看索引,就能看到 name
字段的索引已成功创建 ✅
[
{
"v" : 1,
"key" : { "_id" : 1 },
"name" : "_id_",
"ns" : "test.user"
},
{
"v" : 1,
"key" : { "name" : 1 },
"name" : "name",
"ns" : "test.user"
}
]
🔗 官方文档参考:索引自动创建
2.2 编程式创建索引
除了注解,也可以通过代码动态创建索引,适用于运行时条件判断场景:
mongoOps.indexOps(User.class)
.ensureIndex(new Index().on("name", Direction.ASC));
效果与 @Indexed
一致,但更灵活,比如可以结合环境变量或配置中心动态决定是否建索引。
2.3 复合索引(Compound Indexes)
MongoDB 支持对多个字段建立复合索引,提升多条件查询效率。
使用 @CompoundIndex
和 @CompoundIndexes
定义:
@QueryEntity
@Document
@CompoundIndexes({
@CompoundIndex(name = "email_age", def = "{'email.id' : 1, 'age': 1}")
})
public class User {
//
}
查看实际生成的索引结构:
{
"v" : 1,
"key" : {
"email.id" : 1,
"age" : 1
},
"name" : "email_age",
"ns" : "test.user"
}
📌 注意事项:
@Index
不能用于DBRef
类型字段- 但
DBRef
字段可以参与复合索引(如上例中的email.id
)
3. 常用注解(Common Annotations)
Spring Data MongoDB 提供了一系列注解,用于控制实体映射行为。
3.1 @Transient
标记为 @Transient
的字段不会被持久化到数据库:
public class User {
@Transient
private Integer yearOfBirth;
// getter/setter
}
插入数据示例:
User user = new User();
user.setName("Alex");
user.setYearOfBirth(1985);
mongoTemplate.insert(user);
数据库实际存储内容:
{
"_id" : ObjectId("55d8b30f758fd3c9f374499b"),
"name" : "Alex",
"age" : null
}
yearOfBirth
字段完全消失,查询时返回 null
。
类比 JPA 的
@Transient
,行为一致,很好理解。
3.2 @Field
指定字段在 MongoDB 中的键名(key),实现 Java 字段与 DB 字段的映射:
@Field("email")
private EmailAddress emailAddress;
保存操作:
User user = new User();
user.setName("Brendan");
EmailAddress emailAddress = new EmailAddress();
emailAddress.setValue("brendan@example.com");
user.setEmailAddress(emailAddress);
mongoTemplate.insert(user);
数据库结果:
{
"_id" : ObjectId("55d076d80bad441ed114419d"),
"name" : "Brendan",
"age" : null,
"email" : {
"value" : "brendan@example.com"
}
}
✅ 成功将 emailAddress
映射为 email
键。
3.3 @PersistenceConstructor
与 @Value
这两个注解组合使用,能实现构造器注入 + 表达式默认值,非常强大。
@PersistenceConstructor
public User(String name, @Value("#root.age ?: 0") Integer age, EmailAddress emailAddress) {
this.name = name;
this.age = age;
this.emailAddress = emailAddress;
}
关键点:
@PersistenceConstructor
指定 Spring Data 用哪个构造器还原对象@Value
支持 SpEL 表达式,这里表示:如果age
为 null,则默认赋值为 0
验证逻辑:
User user = new User();
user.setName("Alex");
mongoTemplate.insert(user);
数据库中 age
是 null
:
{
"_id" : ObjectId("55d074ca0bad45f744a71318"),
"name" : "Alex",
"age" : null
}
但查询时:
mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getAge();
返回结果是 0
✅
⚠️ 踩坑提醒:如果你的类有多个构造器,务必用 @PersistenceConstructor
明确指定,否则可能因反射选错构造器导致 NullPointerException
。
4. 转换器(Converters)
MongoConverter
是 Spring Data MongoDB 的核心组件,负责 Java 对象与 DBObject
之间的双向转换。
默认使用 MappingMongoConverter
,它会自动处理类型映射、嵌套对象、集合等。
但有时我们需要更精细的控制,比如:不想保存 _class
字段。
MongoDB 默认会在文档中添加 _class
,用于反序列化时确定类型。但在某些场景下(如跨服务共享数据),我们希望去掉它。
自定义写入转换器
实现 Converter<S, T>
接口,定义从 User
到 DBObject
的转换逻辑:
@Component
public class UserWriterConverter implements Converter<User, DBObject> {
@Override
public DBObject convert(User user) {
DBObject dbObject = new BasicDBObject();
dbObject.put("name", user.getName());
dbObject.put("age", user.getAge());
if (user.getEmailAddress() != null) {
DBObject emailDbObject = new BasicDBObject();
emailDbObject.put("value", user.getEmailAddress().getValue());
dbObject.put("email", emailDbObject);
}
dbObject.removeField("_class"); // 关键:移除 _class
return dbObject;
}
}
注册自定义转换器
在 MongoConfig
中注册:
private List<Converter<?,?>> converters = new ArrayList<>();
@Override
public MongoCustomConversions customConversions() {
converters.add(new UserWriterConverter());
return new MongoCustomConversions(converters);
}
💡 注意:
MongoCustomConversions
是 Spring Data 提供的注册入口,必须通过它才能生效。
XML 配置方式(可选)
如果你还在用 XML 配置,也可以这样写:
<bean id="mongoTemplate"
class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongo" ref="mongo"/>
<constructor-arg ref="mongoConverter" />
<constructor-arg name="databaseName" value="test"/>
</bean>
<mongo:mapping-converter id="mongoConverter" base-package="org.baeldung.converter">
<mongo:custom-converters base-package="com.baeldung.converter" />
</mongo:mapping-converter>
验证效果
保存一个用户:
User user = new User();
user.setName("Chris");
mongoOps.insert(user);
数据库结果(无 _class
):
{
"_id" : ObjectId("55cf09790bad4394db84b853"),
"name" : "Chris",
"age" : null
}
✅ 成功去除了类型元信息。
5. 总结
本文覆盖了 Spring Data MongoDB 的三大实用功能:
- ✅ 索引管理:
@Indexed
、复合索引、编程式创建 - ✅ 常用注解:
@Transient
、@Field
、@PersistenceConstructor + @Value
- ✅ 自定义转换器:控制序列化行为,如移除
_class
这些技巧在实际开发中非常有用,尤其在性能调优和数据模型定制方面。建议集合备用。
所有示例代码均可在 GitHub 获取:
🔗 https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-mongodb