$show=/label

Auto-Generated Field for MongoDB using Spring Boot - Step By Step

SHARE:

Learn how to generate sequential, auto-incremented values with mongodb for the id field and simulate the same behavior as seen in SQL databases using spring boot and mongodb.

Outline:

In this tutorial, we are going to learn how to implement a sequential and auto-generated field for MongoDB in Spring Boot. If you are new to MongoDB and Spring then visit  Spring Data MongoDB Tutorial.

Understanding step by step today about "Auto-Generated Field for MongoDB using Spring Boot"

Spring Boot Tutorials

[update] Updated new errors in this article.
This is a very common scenario that arises when using MongoDB and Spring framework integration work.

When we are using MongoDB databases as part of the Spring Boot application directly we can not use @GeneratedValue annotation in our database model classes.

@GeneratedValue: Marking a field with the @GeneratedValue annotation specifies that value will be automatically generated for that field and mainly this is used on primary key fields. This annotation is available only for JPA API.



Auto-Generated Field for MongoDB using Spring Boot


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

Maven Dependencies for spring boot and mongodb:

We should add the required dependencies in the pom.xml file as below. Get the latest version from here.


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

Using Collections:

1) First, create a collection that will store the auto-incremented value in it. This can be created using either the mongo shell or MongoDB Compass


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

2. Let’s then create a users_db collection. This collection stores the users that are being used.


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

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

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

    @Id
    private long id;

    private String firstName;

    private String lastName;

    private String email;

    public User() { }

    public User(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    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 getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
    
    @Override
    public String toString() {
        return "User{" + "id=" + id + ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + '}';
    }
}

Here SEQUENCE_NAME is declared as constant and annotated with @Transient to prevent it from serializing to persist in the table.



Creating a New Record - Auto-Generated for MongoDB:

1) Creating a UserRepository.

import com.java-w3schools.mongodb.models.User;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository {

}

2) Creating Service


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

    }
}

3) Creating Listener


@Component
public class UserModelListener extends AbstractMongoEventListener {

    private SequenceGeneratorService sequenceGeneratorService;

    @Autowired
    public UserModelListener(SequenceGeneratorService sequenceGeneratorService) {
        this.sequenceGeneratorService = sequenceGeneratorService;
    }

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

4) Calling generateSequence




User user = new User();
user.setId(sequenceGeneratorService.generateSequence(User.SEQUENCE_NAME));
user.setEmail("java-w3schools@example.com");
userRepository.save(user);

Here a unique id will be generated in sequence when we invoke generateSequence() method each time.

TroubleShooting

Problem:

org.springframework.dao.InvalidDataAccessApiUsageException: Cannot autogenerate id of type java.lang.Long for entity of type ua.home.springdata.investigation.entity.Account!
    at org.springframework.data.mongodb.core.MongoTemplate.assertUpdateableIdIfNotSet(MongoTemplate.java:1149)
    at org.springframework.data.mongodb.core.MongoTemplate.doSave(MongoTemplate.java:878)
    at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:833)
    at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:73)
    at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:88)
    at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:442)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:427)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy26.save(Unknown Source)

Suggestion 1:


Mongo ObjectIds don't map to a java Long type.

I see this in the documentation, under 7.6.1:


An id property or field declared as a String in the Java class will be converted to and stored as an ObjectId if possible using a Spring Converter. Valid conversion rules are delegated to the MongoDB Java driver. If it cannot be converted to an ObjectId, then the value will be stored as a string in the database.

An id property or field declared as BigInteger in the Java class will be converted to and stored as an ObjectId using a Spring Converter.
So change id to a String or a BigInteger and remove the strategy argument.

Suggestion 2:


Using @Id as a String works fine.

Make sure that your Repository extends with a String (same type as the @Id):

extends MongoRepository<MyEntity, String>

Suggestion 3:

Add maven dependency:

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

Source:

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

@Document(collection = "ACCOUNTS")
public class Account {

    @Id
    private String id;

    ....rest of properties
}

import org.springframework.data.mongodb.repository.MongoRepository;
public interface AccountRepository extends MongoRepository<Account, String>  {
    //any extra queries needed
}

Conclusion:

We have seen today how to generate sequential auto-incremented values for the id field and simulate the same behavior as seen as in SQL databases or using JPA API.

COMMENTS

BLOGGER

About Us

Author: Venkatesh - I love to learn and share the technical stuff.
Name

accumulo,1,ActiveMQ,2,Adsense,1,API,31,ArrayList,16,Arrays,3,Bean Creation,3,Bean Scopes,1,BiConsumer,1,Blogger Tips,1,Books,1,C Programming,1,Collection,4,Collections,21,Collector,1,Command Line,1,Compile Errors,1,Configurations,7,Constants,1,Control Statements,8,Conversions,5,Core Java,74,Corona India,1,Create,2,CSS,1,Date,2,Date Time API,3,Dictionary,1,Difference,1,Download,1,Eclipse,2,Efficiently,1,Error,1,Errors,1,Exception,1,Exceptions,3,Fast,1,Files,9,Float,1,Font,1,For examples,1,For loop examples,1,For Loop in Java,1,Form,1,Freshers,1,Function,3,Functional Interface,2,Garbage Collector,1,Generics,4,Git,4,Grant,1,Grep,1,HashMap,1,HomeBrew,2,HTML,2,HttpClient,2,Immutable,1,Inner for loops,1,Installation,1,Interview Questions,5,Iterate,2,Jackson API,3,Java,28,Java 10,1,Java 11,5,Java 12,5,Java 13,2,Java 14,2,java 5 For loop,1,Java 8,50,Java 9,1,Java Design Patterns,1,Java Files,1,Java for loop,1,Java Program,2,Java Programs,65,java.lang,5,java.util. function,1,jQuery,1,Kotlin,10,Kotlin Programs,6,Lambda,1,lang,29,Leap Year,1,live updates,1,Mac OS,2,Math,1,Maven,1,Method References,1,Mockito,1,MongoDB,3,Nested for loop,1,Nested for loop examples,1,New Features,1,Operations,1,Optional,4,Oracle,5,Oracle 18C,1,Partition,1,Patterns,1,Programs,1,Property,1,Python,2,Quarkus,1,Read,1,Real Time,1,Recursion,2,Remove,2,Rest API,1,Schedules,1,Serialization,1,Servlet,1,Sorting Techniques,8,Spring,2,Spring Boot,23,Spring Email,1,Spring MVC,1,Stream,3,Streams,12,String,48,String Programs,8,String Revese,1,Swing,1,System,1,Tags,1,Threads,9,Tomcat,1,Tomcat 8,1,Troubleshoot,16,Unix,2,Updates,3,util,5,While Loop,1,
ltr
item
JavaProgramTo.com: Auto-Generated Field for MongoDB using Spring Boot - Step By Step
Auto-Generated Field for MongoDB using Spring Boot - Step By Step
Learn how to generate sequential, auto-incremented values with mongodb for the id field and simulate the same behavior as seen in SQL databases using spring boot and mongodb.
https://4.bp.blogspot.com/-ApK38v3zMfs/XMvQmGMgC0I/AAAAAAAABdQ/rEMmQSEG5mgCMu2gVAT78UHIDBYXKT-LACLcBGAs/s400/Auto-Generated%2BField%2Bfor%2BMongoDB%2Busing%2BSpring%2BBoot.PNG
https://4.bp.blogspot.com/-ApK38v3zMfs/XMvQmGMgC0I/AAAAAAAABdQ/rEMmQSEG5mgCMu2gVAT78UHIDBYXKT-LACLcBGAs/s72-c/Auto-Generated%2BField%2Bfor%2BMongoDB%2Busing%2BSpring%2BBoot.PNG
JavaProgramTo.com
https://www.javaprogramto.com/2019/05/spring-boot-mongodb-auto-generated-field.html
https://www.javaprogramto.com/
https://www.javaprogramto.com/
https://www.javaprogramto.com/2019/05/spring-boot-mongodb-auto-generated-field.html
true
3124782013468838591
UTF-8
Loaded All Posts Not found any posts VIEW ALL Readmore Reply Cancel reply Delete By Home PAGES POSTS View All RECOMMENDED FOR YOU LABEL ARCHIVE SEARCH ALL POSTS Not found any post match with your request Back Home Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sun Mon Tue Wed Thu Fri Sat January February March April May June July August September October November December Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec just now 1 minute ago $$1$$ minutes ago 1 hour ago $$1$$ hours ago Yesterday $$1$$ days ago $$1$$ weeks ago more than 5 weeks ago Followers Follow THIS PREMIUM CONTENT IS LOCKED STEP 1: Share to a social network STEP 2: Click the link on your social network Copy All Code Select All Code All codes were copied to your clipboard Can not copy the codes / texts, please press [CTRL]+[C] (or CMD+C with Mac) to copy Table of Content