Partner – Microsoft – NPI (cat= Spring Boot)
announcement - icon

Azure Spring Apps is a fully managed service from Microsoft (built in collaboration with VMware), focused on building and deploying Spring Boot applications on Azure Cloud without worrying about Kubernetes.

And, the Enterprise plan comes with some interesting features, such as commercial Spring runtime support, a 99.95% SLA and some deep discounts (up to 47%) when you are ready for production.

>> Learn more and deploy your first Spring Boot app to Azure.

You can also ask questions and leave feedback on the Azure Spring Apps GitHub page.

1. Overview

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

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.

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

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

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

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

3. Collections

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:

@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:

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

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

    @Id
    private BigInteger 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. Also for auto-generation of an ObjectId to succeed, the type of the Id property or field in your class must be a String, an ObjectId, or a BigInteger.

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

4. Creating a New Record

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.

Let’s create a SequenceGeneratorService that has 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:

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

To list all the users, we’ll use the 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.

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

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

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

5. Conclusion

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.

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

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

Course – LSD (cat=Persistence)

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE
res – Persistence (eBook) (cat=Persistence)
3 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.