Auto-Generated Field for MongoDB using Spring Boot – 使用Spring Boot为MongoDB自动生成字段

最后修改: 2018年 12月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’re going to learn how to implement a sequential, auto-generated field for MongoDB in Spring Boot.

在本教程中,我们将学习如何在Spring Boot中为MongoDB实现一个连续的、自动生成的字段。

When we’re using MongoDB as the database for a Spring Boot application, we can’t use @GeneratedValue annotation in our models as it’s not available. Hence we need a method to produce the same effect as we’ll have if we’re using JPA and an SQL database.

当我们使用 MongoDB 作为 Spring Boot 应用程序的数据库时,我们无法在模型中使用 @GeneratedValue 注解,因为它不可用。因此,我们需要一种方法来产生与我们使用JPA和SQL数据库时相同的效果。

The general solution to this problem is simple. We’ll create a collection (table) that’ll store the generated sequence for other collections. During the creation of a new record, we’ll use it to fetch the next value.

这个问题的一般解决方案很简单。我们将创建一个集合(表),它将存储其他集合的生成序列。在创建一个新记录的过程中,我们将使用它来获取下一个值。

2. Dependencies

2.依赖性

Let’s add the following spring-boot starters to our pom.xml:

让我们在我们的pom.xml中添加以下spring-boot启动器。

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

The latest version for the dependencies is managed by spring-boot-starter-parent.

依赖关系的最新版本是由spring-boot-starter-parent管理。

3. Collections

3.收藏品

As discussed in the overview, we’ll create a collection that’ll store the auto-incremented sequence for other collections. We’ll call this collection database_sequences. It can be created using either the mongo shell or MongoDB Compass. Let’s create a corresponding model class:

正如在概述中所讨论的,我们将创建一个集合,用于存储其他集合的自动递增序列。我们将这个集合称为database_sequences。它可以使用mongo shell或MongoDB Compass创建。我们来创建一个相应的模型类。

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

    @Id
    private String id;

    private long seq;

    //getters and setters omitted
}

Let’s then create a users collection, and a corresponding model object, that’ll store the details of people that are using our system:

然后让我们创建一个users集合,以及一个相应的模型对象,它将存储使用我们系统的人的详细信息。

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

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

    @Id
    private long id;

    private String email;

    //getters and setters omitted
}

In the User model created above, we added a static field SEQUENCE_NAME, which is a unique reference to the auto-incremented sequence for the users collection.

在上面创建的User模型中,我们添加了一个静态字段SEQUENCE_NAME,,它是对users集合的自动递增序列的唯一引用。

We also annotate it with the @Transient to prevent it from being persisted alongside other properties of the model.

我们还用@Transient来注解它,以防止它与模型的其他属性一起被持久化。

4. Creating a New Record

4.创建一个新的记录

So far, we’ve created the required collections and models. Now, we’ll create a service that’ll generate the auto-incremented value that can be used as id for our entities.

到目前为止,我们已经创建了所需的集合和模型。现在,我们将创建一个服务,它将生成自动递增的值,可以作为我们实体的id

Let’s create a SequenceGeneratorService that has generateSequence():

让我们创建一个SequenceGeneratorService,它有generateSequence()

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;
}

Now, we can use the generateSequence() while creating a new record:

现在,我们可以在创建一个新记录时使用generateSequence()

User user = new User();
user.setId(sequenceGenerator.generateSequence(User.SEQUENCE_NAME));
user.setEmail("john.doe@example.com");
userRepository.save(user);

To list all the users, we’ll use the UserRepository:

为了列出所有的用户,我们将使用UserRepository

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

As it is now, we have to set the id field every time we create a new instance of our model. We can circumvent this process by creating a listener for Spring Data MongoDB lifecycle events.

按照现在的情况,我们必须在每次创建模型的新实例时设置id字段。我们可以通过为 Spring Data MongoDB 生命周期事件创建一个监听器来规避这一过程。

To do that, we’ll create a UserModelListener that extends AbstractMongoEventListener<User> and then we’ll override the onBeforeConvert():

要做到这一点,我们将创建一个UserModelListener,它扩展了AbstractMongoEventListener<User>,然后我们将重写onBeforeConvert()

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

Now, every time we save a new User, the id will be set automatically.

现在,每次我们保存一个新的用户id将被自动设置。

5. Conclusion

5.结论

In conclusion, we’ve seen how to generate sequential, auto-incremented values for the id field and simulate the same behavior as seen in SQL databases.

总之,我们已经看到了如何为id字段生成连续的、自动递增的值,并模拟在SQL数据库中看到的相同行为。

Hibernate uses a similar method for generating auto-incremented values by default.

Hibernate默认使用类似的方法来生成自动递增的值。

As usual, the complete source code is available over on Github.

像往常一样,完整的源代码可在Github上获得。