Common beforeInsert and beforeUpdate methods from

2020-04-23 03:57发布

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.

2条回答
可以哭但决不认输i
2楼-- · 2020-04-23 04:47

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.

查看更多
家丑人穷心不美
3楼-- · 2020-04-23 05:00

Instead of mixins you can use the Event Bus of the Grails Platform plugin to make your app more DRY. The plugin supports adding listeners to GORM events and they can be applied to any given class or interface instance. You can also attach more than one listener and write more concise unit tests.

If you want to share some logic accross domains, they must implement an interface and then create a service with the following method:

// domainService.groovy

@grails.events.Listener(namespace='gorm')
def beforeInsert(DomainInterface domain){
    domain.creator = 'foo'
    domain.updater = 'bar'

    // If the method returns false, then domain.save() won't be called. 
}
查看更多
登录 后发表回答