1. 概述

在本教程中,我们将学习如何在 Spring Boot 项目中为 MongoDB 实现一个顺序自增字段

⚠️ 使用 MongoDB 作为数据库时,无法像 JPA + SQL 那样直接使用 @GeneratedValue 注解。 因此我们需要一种替代方案来实现类似的效果。

✅ 解决方案其实很简单:我们创建一个专门用于存储序列号的集合(collection),每次插入新记录前从中获取下一个自增值即可。

2. 依赖配置

首先,在 pom.xml 中添加以下 Spring Boot Starter:

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

版本管理由 spring-boot-starter-parent 自动处理。

3. 数据结构设计

如前所述,我们要创建一个专门保存序列号的集合,命名为 database_sequences。可以用 mongo shell 或者 MongoDB Compass 手动创建。

对应的 Java 模型类如下:

@Document(collection = "database_sequences")
public class DatabaseSequence {

    @Id
    private String id;

    private long seq;

    // 省略 getter 和 setter
}

然后我们再创建一个用户集合 users 及其对应的模型类:

@Document(collection = "users")
public class User {

    @Transient
    public static final String SEQUENCE_NAME = "users_sequence";

    @Id
    private BigInteger id;

    private String email;

    // 省略 getter 和 setter
}

📌 注意点:

  • SEQUENCE_NAME 是该集合唯一标识的序列名。
  • MongoDB 要求主键字段类型必须是 StringObjectIdBigInteger,才能自动生成 _id
  • 使用 @Transient 注解防止 SEQUENCE_NAME 被持久化到数据库。

4. 实现自增逻辑

现在我们来实现一个服务类,用于生成自增 ID:

public long generateSequence(String seqName) {
    DatabaseSequence counter = mongoOperations.findAndModify(
        query(where("_id").is(seqName)),
        new Update().inc("seq", 1),
        options().returnNew(true).upsert(true),
        DatabaseSequence.class
    );
    return !Objects.isNull(counter) ? counter.getSeq() : 1;
}

使用方式也很简单粗暴:

User user = new User();
user.setId(BigInteger.valueOf(sequenceGenerator.generateSequence(User.SEQUENCE_NAME)));
user.setEmail("[email protected]");
userRepository.save(user);

如果要列出所有用户:

List<User> storedUsers = userRepository.findAll();
storedUsers.forEach(System.out::println);

✅ 进阶优化:自动填充 ID

每次手动设置 ID 太麻烦?可以用 MongoDB 生命周期监听器来简化操作。

创建一个监听器类继承 AbstractMongoEventListener<User> 并重写 onBeforeConvert() 方法:

@Override
public void onBeforeConvert(BeforeConvertEvent<User> event) {
    if (event.getSource().getId().intValue() < 1) {
        event.getSource().setId(BigInteger.valueOf(
            sequenceGenerator.generateSequence(User.SEQUENCE_NAME)
        ));
    }
}

这样,每次保存新的 User 对象时,ID 就会自动填充了,非常优雅!

5. 总结

在这篇文章中我们实现了 MongoDB 中模拟 SQL 自增主键的功能:

✅ 通过一个独立的序列集合保存当前最大值
✅ 利用 findAndModify 原子操作保证并发安全
✅ 结合 Spring Data MongoDB 生命周期事件实现自动填充

💡 Hibernate 在处理自增主键时也采用了类似的机制。

完整代码示例可以在 GitHub 上找到:Spring Boot with MongoDB Auto-Increment


原始标题:Auto-Generated Field for MongoDB using Spring Boot