0

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
Adam Monos
  • 4,287
  • 1
  • 23
  • 25

1 Answers1

1

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?

anorakgirl
  • 668
  • 8
  • 19
  • Yes you are right. It turns out that my second ajax call was calling the controller method twice because it was adding a click() event again rather than replacing the first click event with the second click event. On the second call to the controller method the error was being thrown because the user was deleted with the first call. – user3065675 Jun 11 '14 at 10:09