Spring Boot + MongoDB Auto-Generated Field Example

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.

Related tutorial - Spring Boot MongoDB CRUD Example Tutorial
Learn complete spring boot at https://www.javaguides.net/p/spring-boot-tutorial.html
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.

Define Maven Dependencies

Let's define all the spring boot starter maven dependencies required to build this application in below pom.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath />
        <!-- lookup parent from repository -->
    </parent>
    <groupId>net.springboot.javaguides</groupId>
    <artifactId>springboot-mongodb-crud-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-thymeleaf-web-app</name>
    <description>Demo project for Spring Boot + Mongo DB CRUD Example
 </description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Collections

Let's 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:
package net.guides.springboot.crud.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;


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

    @Id
    private String id;

    private long seq;

    public DatabaseSequence() {}

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public long getSeq() {
        return seq;
    }

    public void setSeq(long seq) {
        this.seq = seq;
    }
}


Let’s now create the Employee model which will be mapped to a Document in the MongoDB database. Create a new package model inside net.guides.springboot.crud(refer project structure section), and add a file Employee.java inside the model package with the following contents -
package net.guides.springboot.crud.model;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "Employee")
public class Employee {

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

    @Id
    private long id;

    @NotBlank
    @Size(max = 100)
    @Indexed(unique = true)
    private String firstName;
    private String lastName;

    @NotBlank
    @Size(max = 100)
    @Indexed(unique = true)
    private String emailId;

    public Employee() {

    }

    public Employee(String firstName, String lastName, String emailId) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.emailId = emailId;
    }

    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmailId() {
        return emailId;
    }
    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", emailId=" + emailId +
            "]";
    }
}
In the Employee model created above, we added a static field SEQUENCE_NAME, which is a unique reference to the auto-incremented sequence for the Emolyee collection.
We also annotate it with the @Transient to prevent it from being persisted alongside other properties of the model.

SequenceGeneratorService - Auto-Generate Sequence

Let's create a service that'll generate the auto-incremented value that can be used as the id for our entities.

package net.guides.springboot.crud.service;

import static org.springframework.data.mongodb.core.FindAndModifyOptions.options;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;

import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import net.guides.springboot.crud.model.DatabaseSequence;

@Service
public class SequenceGeneratorService {

    private MongoOperations mongoOperations;

    @Autowired
    public SequenceGeneratorService(MongoOperations mongoOperations) {
        this.mongoOperations = mongoOperations;
    }

    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;

    }
}

Set Auto-Generated Field

Now, we can use the generateSequence() while creating a new record:
 @PostMapping("/employees")
    public Employee createEmployee(@Valid @RequestBody Employee employee) {
        employee.setId(sequenceGeneratorService.generateSequence(Employee.SEQUENCE_NAME));
        return employeeRepository.save(employee);
    }
Now, every time we save a new Employee, the id will be set automatically.

Complete Example

Spring Boot MongoDB CRUD Example Tutorial - This tutorial shows you step by step how to create CRUD Spring boot restful APIs with MongoDB and also implement a sequential, auto-generated field for MongoDB in Spring Boot.

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. Check out a complete example at Spring Boot MongoDB CRUD Example Tutorial.

Comments