1

I try in Grails service save an object to mongodb:

Cover saveCover = new Cover()
saveCover.id = url
saveCover.url = url
saveCover.name = name
saveCover.sku = sku
saveCover.price = price

saveCover.save()

Cover domain looks like this:

class Cover {

    String id
    String name
    String url
    String sku
    String price
}

So I want to have custom id based on url, but during save process I get error:

Could not commit Datastore transaction; nested exception is org.grails.datastore.mapping.core.OptimisticLockingException: The instance was updated by another user while you were editing

But if I didn`t use setters and just pass all values in constructor, the exception is gone. Why?

4

2 回答 2

4

As reported in the documentation here:

Note that if you manually assign an identifier, then you will need to use the insert method instead of the save method, otherwise GORM can't work out whether you are trying to achieve an insert or an update

so you need to use insert method instead of save when id generator is assigned

cover.insert(failOnError: true)

if you do not define the mapping like this:

static mapping = {
    id generator: 'assigned'
}

and will use insert method you'll get an auto-generated objectId:

"_id" : "5496e904e4b03b155725ebdb"
于 2014-12-21T15:42:57.417 回答
1

This exception occurs when you assign an id to a new model and try to save it because GORM thinks it should be doing an update.

Why this exception occurs

When I ran into this issue I was using 1.3.0 of the grails-mongo plugin. That uses 1.1.9 of the grails datastore core code. I noticed that the exception gets generated on line 847(ish) of NativeEntryEntityPersister. This code updates an existing domain object in the db.

Above that on line 790 is where isUpdate is created which is used to see if it's an update or not. isInsert is false as it is only true when an insert is forced and readObjectIdentifier will return the id that has been assigned to the object so isUpdate will end up evaluating as true.

Fixing the exception

Thanks to && !isInsert on line 791 if you force an insert the insert code will get called and sure enough the exception will go away. However when I did this the assigned id wasn't saved and instead a generated object id was used. I saw that the fix for this was on line 803 where it checks to see if the generator is set to "assigned".

To fix that you can add the following mapping.

class Cover {
    String id
    String name
    String url
    String sku
    String price
    static mapping = {
        id generator: 'assigned'
    }
}

A side effect of this is that you will always need to assign an id for new Cover domain objects.

于 2013-12-09T13:56:36.620 回答