Search code examples
grailsgrails-orm

Grails domain class beforeDelete is not working as transactional


I have an user domain class with attributes like username, fullName, ... etc. and a UserRole association class.

In my domain class I have the following code on the beforeDelete method

def beforeDelete() {
    UserRole.removeAll(this);
}

In the UserRole class I have the removeAll method like this:

static void removeAll(User user) {
    executeUpdate 'DELETE FROM UserRole WHERE user=:user', [user: user]
}

The call to the delete method is done in my UserService class

def delete(User userInstance){
   userInstance.delete()
}

What I am expecting is: When deletion fails a rollback should be performed, but even when deletion fails all the UserRole associations are being deleted.

Am I missing something? The beforeDelete method is not wrapped in the same transaction as the userService.delete(User userInstance) method?

Or should I move the UserRole.removeAll() call to the UserService class?

Grails Version: 2.3.11

Hibernate: 3.6.10.16


Solution

  • Nothing in Grails is transactional without configuration except services. A service with no @Transactional annotations and no static transactional property is transactional. Adding one or @Transactional annotations lets you customize the behavior for the service, and/or per-method. The only way to make a service non-transactional is to remove all annotations and set static transactional = false. Nothing else in a Grails app is transactional.

    You can use @Transactional in controllers and the domain class withTransaction method, but both are hackish and services should always be used.

    You can use dependency injection in domain classes, so I'd move all of the update code to a service method and call that from the domain class event method:

    class MyDomain {
    
       ...
    
       def myService
    
       def beforeDelete() {
          myService.beforeDeleteMyDomain this
       }
    }
    

    Or, since you're deleting in a service already, you can just combine everything in one method:

    def delete(User userInstance){
       UserRole.removeAll userInstance
       userInstance.delete()
    }