Search code examples
grailsgroovygrails-ormmixins

Common beforeInsert and beforeUpdate methods from Mixin for common domain columns


Most of the domain objects our company uses will have some common properties. These represent the user that created the object, the user that last updated the object, and the program they used to do it.

In the interest of DRYing out my domain classes, I want to find some way to add the same beforeInsert and beforeUpdate logic to all domain classes that have these columns without interfering with those that don't.

How I'd like to do it is using a Mixin with its own beforeInsert and beforeUpdate methods. I know you can use Mixins on domain classes.

package my.com

import my.com.DomainMixin

@Mixin(DomainMixin)
class MyClass {
    String foo
    String creator
    String updater

    static constraints = {
        creator nullable:false
        updater nullable:false
    }
}


package my.com
class DomainMixin {
    def beforeInsert() {
        this.creator = 'foo'
        this.updater = 'foo'
    }

    def beforeUpdate() {
        this.updater = 'bar'
    }
}

Unit tests would indicate that the beforeInsert method isn't actually getting fired when implemented this way.

Side note: I also know it's possible to add the methods in a BootStrap.groovy file using the metaClass, but my curiosity has gotten the better of me and I really want to see if the mixin works. Feel free to tell me that this is the better way to do it and I ought not muddle where man ought not.


Solution

  • FYI, use of groovy.lang.Mixin is strongly discouraged (by the Groovy project leader, for one). If you have to use mixins you should use grails.util.Mixin instead. One thing I don't like about your mixin approach is the implicit and unenforced assumption that the target of the mixin has creator and updater properties

    Personally, I would probably just use plain-old inheritance for this, e.g.

    abstract class Audit {
    
        String creator
        String updater
    
        def beforeInsert() {
            this.creator = 'foo'
            this.updater = 'foo'
        }
    
        def beforeUpdate() {
            this.updater = 'bar'
        }
    
        static constraints = {
            creator nullable: false
            updater nullable: false
        }
    }
    

    any domain classes that need to be audited would simply extend Audit. An alternative (and preferable) approach would be to use a trait rather than an abstract base class, but you'll need to be using a fairly recent Grails version in order to do this.