Main Tutorials

Spring Data MongoDB – Auto Sequence ID example

In this tutorial, we will show you how to generate an auto-incrementing sequence id in MongoDB + Spring Data environment.

Tools used in this project :

  1. Spring Data MongoDB 1.2.1.RELEASE
  2. MongoDB 2.4.5
  3. Eclipse 4.2
  4. Maven 3

At the end of this tutorial, if the collection name “hosting” is saved, a new auto-incrementing sequence id will be assigned. Below is the Java code snippet to generate the sequence id.


  public long getNextSequenceId(String key) {

	Query query = new Query(Criteria.where("_id").is(key));

        Update update = new Update();
	update.inc("seq", 1);

	FindAndModifyOptions options = new FindAndModifyOptions();
	options.returnNew(true);

	SequenceId seqId = 
            mongoOperation.findAndModify(query, update, options, SequenceId.class);

	return seqId.getSeq();

  }	

1. Project Structure

Review the project directory structure, a standard Maven project.

springdata-auto-sequence-id

2. Maven Pom

In case you are interested at the project dependencies.

pom.xml

<project ...>
	<properties>
		<jdk.version>1.6</jdk.version>
		<spring.version>3.2.2.RELEASE</spring.version>
		<mongojavadriver.version>2.11.1</mongojavadriver.version>
		<springdata.version>1.2.1.RELEASE</springdata.version>
	</properties>

	<dependencies>

		<!-- Spring Core -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- need this for @Configuration -->
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2.2</version>
		</dependency>

		<!-- Spring Data for MongoDB -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-mongodb</artifactId>
			<version>${springdata.version}</version>
		</dependency>

		<!-- Java MongoDB Driver -->
		<dependency>
			<groupId>org.mongodb</groupId>
			<artifactId>mongo-java-driver</artifactId>
			<version>${mongojavadriver.version}</version>
		</dependency>

	</dependencies>
	<build>
		<finalName>SpringData</finalName>
		<plugins>
		  <plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>2.3.2</version>
			<configuration>
				<source>${jdk.version}</source>
				<target>${jdk.version}</target>
			</configuration>
		  </plugin>
		</plugins>
	</build>

</project>

3. Sequence Collection

We create a collection name “sequence” to store the auto increase sequence id. Refer to the SequenceDaoImpl.java below, it shows you the code to generate the sequence id.

Note
Create the “sequence” collection in your MongoDB first!


	db.sequence.insert({_id: "hosting",seq: 0})
SequenceId.java

package com.mkyong.seq.model;

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

@Document(collection = "sequence")
public class SequenceId {

	@Id
	private String id;

	private long seq;

	//get, set, toString...
}
SequenceDao.java

package com.mkyong.seq.dao;

import com.mkyong.seq.exception.SequenceException;

public interface SequenceDao {

	long getNextSequenceId(String key) throws SequenceException;

}
SequenceDaoImpl.java

package com.mkyong.seq.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;

import com.mkyong.seq.exception.SequenceException;
import com.mkyong.seq.model.SequenceId;

@Repository
public class SequenceDaoImpl implements SequenceDao {

	@Autowired
	private MongoOperations mongoOperation;

	@Override
	public long getNextSequenceId(String key) throws SequenceException {
		
	  //get sequence id
	  Query query = new Query(Criteria.where("_id").is(key));

	  //increase sequence id by 1
	  Update update = new Update();
	  update.inc("seq", 1);

	  //return new increased id
	  FindAndModifyOptions options = new FindAndModifyOptions();
	  options.returnNew(true);

	  //this is the magic happened.
	  SequenceId seqId = 
            mongoOperation.findAndModify(query, update, options, SequenceId.class);

	  //if no id, throws SequenceException
          //optional, just a way to tell user when the sequence id is failed to generate.
	  if (seqId == null) {
		throw new SequenceException("Unable to get sequence id for key : " + key);
	  }

	  return seqId.getSeq();

	}

}
SequenceException.java

package com.mkyong.seq.exception;

public class SequenceException extends RuntimeException {

	private static final long serialVersionUID = 1L;

	private String errCode;
	private String errMsg;

	//get, set...
	public SequenceException(String errMsg) {
		this.errMsg = errMsg;
	}

}

4. Get the Sequence ID

To get the sequence id, uses sequenceDao.getNextSequenceId("key").

HostingBo.java

package com.mkyong.hosting.bo;

import com.mkyong.seq.exception.SequenceException;

public interface HostingBo {

	void save(String name) throws SequenceException;

}
HostingBoImpl.java

package com.mkyong.hosting.bo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mkyong.hosting.dao.HostingDao;
import com.mkyong.hosting.model.Hosting;
import com.mkyong.seq.dao.SequenceDao;
import com.mkyong.seq.exception.SequenceException;

@Service
public class HostingBoImpl implements HostingBo {

	private static final String HOSTING_SEQ_KEY = "hosting";

	@Autowired
	private SequenceDao sequenceDao;

	@Autowired
	private HostingDao hostingDao;

	@Override
	public void save(String name) throws SequenceException {

		Hosting hosting = new Hosting();

		hosting.setId(sequenceDao.getNextSequenceId(HOSTING_SEQ_KEY));
		hosting.setName(name);
		hostingDao.save(hosting);

		System.out.println(hosting);

	}

}

5. Testing

Run a simple test.


package com.mkyong;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.mkyong.config.AppConfig;
import com.mkyong.hosting.bo.HostingBo;
import com.mkyong.seq.exception.SequenceException;

public class App {

  public static void main(String[] args) {

	ApplicationContext ctx = 
            new AnnotationConfigApplicationContext(AppConfig.class);
	HostingBo hostingBo = (HostingBo) ctx.getBean("hostingBoImpl");

	try {

		hostingBo.save("cloud.google.com");
		hostingBo.save("heroku.com");
		hostingBo.save("cloudbees.com");

	} catch (SequenceException e) {
		System.out.println(e.getErrMsg());
	}

  }
}

Output – Java console


Hosting [id=1, name=cloud.google.com]
Hosting [id=2, name=heroku.com]
Hosting [id=3, name=cloudbees.com]

MongoDB console.


>mongo

> db.sequence.find()
{ "_id" : "hosting", "seq" : 3 }

> db.hosting.find()
{ "_id" : NumberLong(1), "_class" : "com.mkyong.hosting.model.Hosting", "name" : "cloud.google.com" }
{ "_id" : NumberLong(2), "_class" : "com.mkyong.hosting.model.Hosting", "name" : "heroku.com" }
{ "_id" : NumberLong(3), "_class" : "com.mkyong.hosting.model.Hosting", "name" : "cloudbees.com" }
>

6. FAQs

Q. SequenceException – Unable to get sequence id for key : hosting ?
A. Remember to create the “sequence” collection!


	db.sequence.insert({_id: "hosting",seq: 0})

Download Source Code

References

  1. MongoDB – Create an Auto-Incrementing Sequence Field

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
15 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Christophe
9 years ago

Is this solution threadsafe? Could it be possible that two threads obtain the same sequence from getNextSequence() if called simultaneously?

Martin Becker
9 years ago

Hi, thanks for sharing this tutorial! I am not sure if this will work for concurrent access from one or more threads or sites. Is your approach intended for such a scenario?

Constantino Cronemberger
5 years ago
Reply to  Martin Becker

According to MongoDB docs the operator $inc is atomic: “$inc is an atomic operation within a single document.”

Gaurav Ashara
9 years ago

Hey very nice post .. I am stuck at once place, Where actually i wanted to store data in gridfs collections and to set “_id” field with auto generated sequence id in spring data.

Its provide facility to insert data like

GridFSFile file=gridOperation.store(new FileInputStream(data.getMyDoc()),data.getMyDocFileName(),data.getMyDocFileName(),metaData);

Actually i checked that spring mongodb API for gridfs didn’t provide us facility to insert with Bean(Collection Bean)
So how can i set auto generated id with Spring data mongodb API.

Amrit Agrawal
2 years ago

When we are deleting a record, it is not. decrementing by 1

pranav
3 years ago

thanks brother dont forget this people
db.sequence.insert({_id: “hosting”,seq: 0})

Tomáš Hampl
7 years ago

if you want to auto create sequence use options.upsert(true);

Prasanna Ganesh
7 years ago

Mainly we have to update Spring version and Spring Data version

1.8
4.3.0.RELEASE
2.11.1
1.8.2.RELEASE

salman farisi
8 years ago

hosting.setId(sequenceDao.getNextSequenceId(HOSTING_SEQ_KEY)); why is not errors when required String but you gave int ?

Jeremie
8 years ago

thank you for all your tutorials that are easy to use, simply efficient and cover so many topics. I’s really amazing how I come so often onto your website after looking for something on Google! Thumb up!

DJ
9 years ago

This is very helpful. Thanks.

In the result, I am getting “_class” : “com.mkyong.hosting.model.Hosting”. Is there any way we can get rid of this.

Roma
10 years ago

The link does not meet the content . Please fix it. Thanks!)

mkyong
9 years ago
Reply to  Roma

May I know which link?

Carlos Henrique Latanza
8 years ago
Reply to  mkyong

“Spring Data MongoDB hello world example”

Misael
10 years ago

Great post! Thanks so much for sharing.