0

I have got a very simple gsp-Page with a list of objects and the possibility to edit each of them by clicking on a row. Once clicked a row I fetch the corresponding data via an AJAX call an present the details in some textfields below the table.

I then click on the "update" button resulting in another AJAX POST request sending the data to the corresponding controller which looks like the following:

def update(Role roleInstance) {
    if (roleInstance == null) {
        notFound()
        return
    }

    if (roleInstance.hasErrors()) {
        response.status = 420
        render template: "editForm", model: [roleInstance: roleInstance]
        return
    }

    roleService.update(roleInstance)

    flash.message = message(code: 'default.updated.message', args: [message(code: 'aedb.role.label'), roleInstance.authority])
    render template: "roleTable", model: [roleInstanceList:Role.list()]
}

Everything seems works fine - the domain objects are updated correctly in the database.

There's only one thing I don't understand: Everytime I click on a row in the table in order to fetch the data, change something and subsequently click the update button I get the StaleObjectStateException. If click the update button again the update succeeds without problem.

Currently, I'm saving the objects using the save method of the RoleService. Before that I tried to save it directly from the controller with roleInstance.save flush:true. However, then I got the same error when I did two updates of the same object quite fast after each other.

EDIT:

Following the source for my edit action in the RoleController:

def edit(Role roleInstance) {
    if (roleInstance == null) {
        notFound()
        return
    }

    def notAssignedPermissions = Permission.list() - roleInstance.getPermissions()
    render template: "editForm", model: [roleInstance: roleInstance, notAssignedPermissions: notAssignedPermissions]
}

And the RoleService which is quite a simple class in my case:

@Transactional
class RoleService {

    @Transactional(readOnly = true)
    def getRole(id) {
        Role.get(id)
    }

    @Transactional
    def update(role) {
        role.save()
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
gabriel
  • 347
  • 3
  • 18
  • 1
    *However, then I got the same error when I did two updates of the same object quite fast after each other.* This actually makes perfect sense. It provides data integrity. Without this the latest update to an instance would trample over the previous update indiscriminately. Ultimately the question becomes: how do you want to resolve the conflict of multiple processes attempting to update the same object at approximately the same time? – Emmanuel Rosa Nov 19 '15 at 16:17
  • I have to admit that I thought something similar. But considering the case when I'm using the RoleService - here it doesn't really makes sense to me that it happens every time I try to load and then do the first (!) update. – gabriel Nov 20 '15 at 07:14
  • Can you add the relevant service code? – Emmanuel Rosa Nov 20 '15 at 14:42
  • Concerning your first comment - I have now added `role.lock()` just before `role.save()`. With that pessimistic locking it obviously works now if I edit only one object. However, once I want to change another object I get the `StaleObjectStateException` and I'm not able to alter it at all. – gabriel Nov 23 '15 at 14:57
  • Ok, the other object's `version` attribute was `NULL` - thus, I was not able to update them. Nevertheless, the optimistic/pessimistic locking mechanism is complicated! Anyone, with a solution that works best? – gabriel Nov 24 '15 at 10:15

0 Answers0