grails ajax call row-was-updated-or-deleted-by-another-transaction-or-unsaved-value-mapping-was

639 Views Asked by At

I am using Grails 2.3.5 and I have a controller which contains a ajaxDelete method. This method receives the name as a String of the entity i want to delete. I am using a service to delete the entity which I call from within my controller.

The first time the services deleteSvnUser method is called the SVN user is deleted and my div on the page is updated with a alert stating that the user has been deleted. The second time I delete a user i get the following error:

row-was-updated-or-deleted-by-another-transaction-or-unsaved-value-mapping-was...

I have tried a couple of things to get round this:

  • Adding the @transactional annotation to my service (which I shouldn't have to because it should be transactional by default).
  • flushing my deletes (flush:true)
  • Refreshing the entity before i delete it (.refresh)
  • Locking the entity when I retrieve it (entity.lock(id))

None of the above have worked. I'm not sure what else to do to get around this. Can anyone help? my code is below:

Controller

class SvnUserController {

def svnUserService  

def ajaxDelete() {

    if (!params.selectedSvnUser) {
        request.error = "The selected Svn User does not exist"
        render(template:"svnUserList", model:[svnUserInstanceList: SvnUser.list(params).sort{it.name}, svnUserInstanceTotal: SvnUser.count()])              
        return
    }

        String outcome = svnUserService.deleteSvnUser(params.selectedSvnUser)

        switch (outcome){

            case "":
                request.message = "Svn User ${params.selectedSvnUser} deleted"
                break

            default: request.error = outcome
        }

    }        

    def svnUsers =  svnUserService.getAllSvnUsers(params)
    render(template:"svnUserList", model:[svnUserInstanceList: svnUsers, svnUserInstanceTotal: svnUsers.size()])
    return
}
}

Service

class SvnUserService {

 public String deleteSvnUser(String userName) {

    String outcome = ""

    try {

        SvnUser svnUserInstance = SvnUser.findByName(userName)
        def groups = SvnGroupController.findAllBySvnUserName(userName)

        groups.each { SvnGroup group ->

            group.removeFromSvnUsers(svnUserInstance)
            group.save()
        }

        def userPermissionsList = UserPermission.findAllBySvnUser(svnUserInstance)

        userPermissionsList.each {
            def repoDirectory = it.repositoryDirectory
            repoDirectory.removeFromUserPermissions(it)
            it.delete()
        }

        svnUserInstance.merge()
        svnUserInstance.delete()            
    }
    catch (DataIntegrityViolationException e) {
        outcome = "A error occurred while attempting to delete the Svn User from file."
    }


    return outcome
}

}

The stacktrace is as follows:

Error |
2014-06-10 15:10:07,394 [http-bio-8080-exec-6] ERROR errors.GrailsExceptionResolver  -     StaleObjectStateException occurred when processing request: [POST]  /subzero/svnUser/ajaxDelete - parameters:
selectedSvnUser: ruby2
Row was updated or deleted by another transaction (or unsaved-value mapping was  incorrect): [com.uk.nmi.subzero.SvnGroup#2]. Stacktrace follows:
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.uk.nmi.subzero.SvnGroup#2]
Line | Method
->>   72 | deleteSvnUser in com.uk.nmi.subzero.SvnUserService
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|     87 | ajaxDelete    in com.uk.nmi.subzero.SvnUserController
|    200 | doFilter . .  in grails.plugin.cache.web.filter.PageFragmentCachingFilter
|     63 | doFilter      in grails.plugin.cache.web.filter.AbstractFilter
|   1145 | runWorker . . in java.util.concurrent.ThreadPoolExecutor
|    615 | run           in java.util.concurrent.ThreadPoolExecutor$Worker
^    744 | run . . . . . in java.lang.Thread
1

There are 1 best solutions below

1
On BEST ANSWER

I can't see any problem with your code. Grails should handle the transaction fine.

If your object mapping is set up correctly, the svnUserInstance.delete() call should delete the child objects too, so I don't think you even need the lines between svnUserInstance = SvnUser.findByName(userName) and the delete.

How are you calling the ajaxDelete? Is there any chance it is being called incorrectly the second time?