Search code examples
mongodbspring-bootspring-dataspring-data-mongodb

How can we create Auto generated field for mongodb using spring boot


I write some code.I want to make questionId field in BaseQuestion Class as Autogenerated.Any solution for that? I am not using jpa jar.so i can't use @Generatedvalue annotation.So how we show here this field is auto generated. code is below.

pom.xml

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>audit_project</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <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-thymeleaf</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-thymeleaf</artifactId>
    </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.11.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

BaseQuestion.java

package model;

import java.util.List;

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

@Document(collection = "basequestion")
public class BaseQuestion {
      @ID
    private String id;
    private int questionId;
    private String responseType;
    private boolean required;
    private boolean active;
    private String questionCode;
    private QuestionText questionText;
    private String category;
    private List<Responses> responses;

    public QuestionText getQuestionText() {
        return questionText;
    }

    public void setQuestionText(QuestionText questionText) {
        this.questionText = questionText;
    }

    public List<Responses> getResponses() {
        return responses;
    }

    public void setResponses(List<Responses> responses) {
        this.responses = responses;
    }

    public int getQuestionId() {
        return questionId;
    }

    public void setQuestionId(int questionId) {
        this.questionId = questionId;
    }

    public String getResponseType() {
        return responseType;
    }

    public void setResponseType(String responseType) {
        this.responseType = responseType;
    }

    public boolean getRequired() {
        return required;
    }

    public void setRequired(boolean required) {
        this.required = required;
    }

    public String getQuestionCode() {
        return questionCode;
    }

    public void setQuestionCode(String questionCode) {
        this.questionCode = questionCode;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }
}

AuditProjectRepository.java

package repository;

import org.springframework.data.mongodb.repository.MongoRepository;

import model.BaseQuestion;

public interface AuditProjectRepository extends MongoRepository<BaseQuestion, String> {

    public BaseQuestion findByQuestionId(int questionId);

    public BaseQuestion findByQuestionCode(String questionCode);

    public Long deleteByQuestionId(int questionid);

}

Solution

  • MongoDB came with all sophisticated ObjectId generation feature, but often you just jumped the ship from relational database, and you still want an easy to read / communicate numeric identifier field which automatically increments every time new record is inserted.

    One neat suggestion from MongoDB tutorial is to use a counter collection with a ‘counter name’ as its id, and a ‘seq’ field to store the last used number.

    When developing using Spring Data MongoDB, this neat trick can be written as a simple service. Here I used the collection name as the counter name so it’s easy to guess / remember.

    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 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 com.model.CustomSequences;
    
    
    @Service
    public class NextSequenceService {
        @Autowired private MongoOperations mongo;
    
        public int getNextSequence(String seqName)
        {
            CustomSequences counter = mongo.findAndModify(
                query(where("_id").is(seqName)),
                new Update().inc("seq",1),
                options().returnNew(true).upsert(true),
                CustomSequences.class);
            return counter.getSeq();
        }
    }
    

    CustomSequences is just a simple class representing the collection. Please beware the usage of int data type, this will limit to 2^31 entries maximum.

    import org.springframework.data.annotation.Id;
    import org.springframework.data.mongodb.core.mapping.Document;
    
    @Document(collection = "customSequences")
    public class CustomSequences {
        @Id
        private String id;
        private int seq;
    
    // getters and setters
    }
    

    Then when inserting a new entry (with help of Spring MongoDB Repository support), just set the id field like this before you save it

    BaseQuestion baseQuestion = new BaseQuestion();
    baseQuestion.setQuestionId(nextSequenceService.getNextSequence("customSequences"));
    /* Rest all values */
    
    baseQuestionRepository.save(baseQuestion);
    

    If you don't like this way then you need to use MongoDBEvents and use onBeforeConvert to generate automated value using same above approach.

    Also above approach is threadsafe as findAndModify() is a thread safe atomic method