MongoDB suggests 2 ways to handle that scenario: auto-increment-optimistic-loop
- Use Counters Collection
Create a document to hold your sequence number
db.counters.insert(
{
_id: "mySequenceName",
seq: 0
}
)
Create a getNextSequenceNumber javascript function in mongodb
function getNextSequence() {
var ret = db.counters.findAndModify(
{
query: { _id: mySequenceName },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
Before inserting your document, get the nextSequence number:
BasicDBObject basicDbObject = new BasicDBObject();
basicDbObject.append( "$eval" , "getNextSequence()" );
CommandResult result = mongoTemplate.executeCommand(basicDbObject);
Object seqno = result.get("seq");
Insert your document:
Mode model...
model.setId(Long.parseLong(seqno));
repository.save(model);
- Optimistic Loop
Create a custom insertDocument mongodb function
The other approach requires that you bypass spring-data for the insertion. It's all done in a mongodb javascript function (that you need to create).
The function will first retrieve the largest _id for the document your are inserting:
var cursor = targetCollection.find( {}, { _id: 1 } ).sort( { _id: -1 } ).limit(1);function insertDocument(doc, targetCollection) {
var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1;
Then, you can insert the document using the sequence number:
var results = targetCollection.insert(doc);
However, because the sequence number might have been used by another simultaneous insertion, you need to check for an error, and repeat the process if needed, which is why the whole function runs in a while(1)
loop.
The complete function:
function insertDocument(doc, targetCollection) {
while (1) {
var cursor = targetCollection.find( {}, { _id: 1 } ).sort( { _id: -1 } ).limit(1);
var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1;
doc._id = seq;
var results = targetCollection.insert(doc);
if( results.hasWriteError() ) {
if( results.writeError.code == 11000 /* dup key */ )
continue;
else
print( "unexpected error inserting data: " + tojson( results ) );
}
break;
}
}
Call the insertDocument mongodb function
Now, from java you would call the javascript function insertDocument
. To call the mongo stored proc with a parameter, I believe doEval may be of use:
MongoClient mongoClient = new MongoClient();
DB db = mongoClient.getDB("dbName");
Model m = new Model(...);
CommandResult result = db.doEval("insertDocument", model);