Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring mongorepository save throws duplicate key exception

I am using the java and Spring. As a test, I query an object by id, then try to save the same object without updating anything. I get a duplicate key exception when I do this. According to what I've read, MongoRepository.save() should do an insert if the _id is null and an update otherwise. Clearly, I should get an update.

A bit of code:

// Succeeds
Datatype sut = mongoRepository.findOne("569eac0dd4c623dc65508679");  

// Fails with duplicate key.
mongoRepository.save(sut);  

Why? Repeat the above with object of other classes and they work. How can I trouble shoot this? I don't see how to break it down and isolate the problem.

Thanks

The error:

27906 [http-bio-8080-exec-3] 2016-05-02 13:00:26,304 DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver -

Resolving exception from handler 
[
  public gov.nist.healthcare.tools.hl7.v2.igamt.lite.web.DatatypeSaveResponse 
gov.nist.healthcare.tools.hl7.v2.igamt.lite.web.controller.DatatypeController.save(
  gov.nist.healthcare.tools.hl7.v2.igamt.lite.domain.Datatype) 
throws gov.nist.healthcare.tools.hl7.v2.igamt.lite.web.exception.DatatypeSaveException
]:
org.springframework.dao.DuplicateKeyException: {
   "serverUsed" : "127.0.0.1:27017" ,
   "ok" : 1 ,
   "n" : 0 , 
   "err" : "E11000 duplicate key error index: igl.datatype.$_id_ dup key: { : ObjectId('569eac0dd4c623dc65508679') }" ,
   "code" : 11000};
nested exception is com.mongodb.MongoException$DuplicateKey: {
  "serverUsed" : "127.0.0.1:27017" ,
  "ok" : 1 ,
  "n" : 0 ,
  "err" : "E11000 duplicate key error index: igl.datatype.$_id_ dup key: { : ObjectId('569eac0dd4c623dc65508679') }" ,
  "code" : 11000}

...repeats

I just made a discovery. When saving as shown above, spring attempts an insert, this even though _id is populated.

When saving other objects ( not shown, but similar), spring performs, an update, and yes _id is again populated.

Why the difference? The documentation says spring should update when _id is populated and insert when it is not.

Is there anything else that can be causing this? Something in my object? perhaps my read converter?

Update: I just met with the team. Upon scrutiny we determined we no longer need read converters. Problem solved by another means.

like image 458
Geoffry Roberts Avatar asked May 03 '16 00:05

Geoffry Roberts


2 Answers

In my case the issue was that I added the version for my data model class.

@Version
private Long version;

The old documents did not have one, it resolved to null, and MongoTemplate decided that this is a new document.

In this case just initialize the version with the default value (private Long version = 0;).

like image 153
Reynard Avatar answered Nov 19 '22 10:11

Reynard


When using read converters or write converters you can solve the issue by ensuring the object to be saved contains a non-null id field.

The SimpleMongoRepository checks if the entity is new before performing a conversion. In our instance, we had an object that did not have an id field and the write converter would add it.

Adding a populated id field to the object informs the SimpleMongoRepository to call save instead of insert.

The decision happens here. Your code may vary by Spring version. I hope this helps.

@Override
public <S extends T> S save(S entity) {

    Assert.notNull(entity, "Entity must not be null!");

    if (entityInformation.isNew(entity)) {
        return mongoOperations.insert(entity, entityInformation.getCollectionName());
    }

    return mongoOperations.save(entity, entityInformation.getCollectionName());
}
like image 45
Joe Terzieva Avatar answered Nov 19 '22 11:11

Joe Terzieva