Grails 2.5.0第二级抽象域类创建它自己的表,即使它不应该

时间:2015-06-25 11:35:09

标签: hibernate grails inheritance

如果我创建一个这样的抽象域类:

abstract class DomainBase {

    LocalDateTime created = LocalDateTime.now()
    LocalDateTime updated

    static constraints = {
        created nullable: false
        updated nullable: true
    }

    static mapping = {
        tablePerConcreteClass true
        created column: 'CREATED'
        updated column: 'UPDATED'
    }

    def beforeUpdate() {
        this.updated = LocalDateTime.now()
    }
}

然后我可以使用它扩展其他域类,它们将继承所有内容(属性,约束,映射和拦截器),并且生成的数据库不包含具体的DOMAIN_BASE表。一切都按预期工作。

但是,如果我创建另一个扩展DomainBase的抽象类,例如:

abstract class EntityBase extends DomainBase {
    User createdBy
    Boolean active = true

    static constraints = {
        createdBy nullable: false
        active nullable: true
    }

    static mapping = {
        tablePerConcreteClass true
        createdBy column: 'CREATED_BY_ID'
        active column: 'ACTIVE'
    }
}

然后生成的数据库将具有一个具体的ENTITY_BASE表。这就是问题所在。

我可以收集的建议解决方案是让基类是普通的POGO而不是域对象,但是映射不是继承的,而且我很懒,不能将映射粘贴到我创建的每个域类。

此外,如果我尝试使这些域对象成为特征,我甚至无法运行应用程序,因为它在编译期间会破坏NPE。

有没有简单而优雅的方法来解决这个问题?

2 个答案:

答案 0 :(得分:1)

据我所知,你不能继承Grails 2.4.x中的映射,它认为这在2.5.0中是相同的......

作为一种解决方法,您可以使用和更改域类的脚手架模板。

答案 1 :(得分:0)

我目前对此感到满意的解决方案是This SO answer的改编仅包含在“Utility”闭包中,看起来像这样:

/**
 * Domain related utilities
 */
class DomainUtil {
    private DomainUtil() {}

    /**
     * Utility for inheriting mapping definitions from non-domain classes (classes defined in src/groovy)<br/>
     * Adapted from <a href="https://stackoverflow.com/a/18339655/1182835">https://stackoverflow.com/a/18339655/1182835</a>
     * @param source - source class from which to inherit the mappings
     * @param destination - closure to which the mappings will be added
     */
    static def inheritDomainMappingFrom = { source, destination ->
        def copyMapping = source.mapping.clone()
        copyMapping.delegate = destination
        copyMapping.call()
    }
}

它的用法如下:

给定一些抽象类Foo,它具有映射闭包并存储在src/groovy内,因此它不被视为域类:

abstract class Foo {
    LocalDate prop1
    Boolean prop2

    static mapping = {
        prop1 column: "PROP1_DATE", type: PersistentLocalDate
        prop2 column: "IS_PROP2"
    }
}

继承Bar的域类Foo

class Bar extends Foo {
    Integer prop3
    String prop4
    String prop5

    static mapping = {
        DomainUtil.inheritDomainMappingFrom(Foo, delegate)
        prop3 column: 'PROP3'
        prop4 column: 'PROP4'
        prop5 column: 'PROP5'
    }
}

使用DomainUtil.inheritDomainMappingFrom(Foo, delegate)我的Bar域类将继承Foo抽象基类的所有映射定义。对我来说,这是一个充分的妥协,因为它更容易编写,也可以让加入或维护项目的人理解。

Yannic-AT建议的

Extending Scaffolding Templates也是一种可行的解决方案,但它包含一些让我失望的缺点:

  1. 它不是重构友好的 - 如果在完成大量开发之后需要添加某些属性/约束/映射/拦截器,那么所有更改都需要追溯添加到所有现有类中
  2. 不遵循DRY原则 - 通过修改模板,我们所做的只是指示grails域构建器将我们新添加的代码复制并粘贴到我们创建的每个新域类中。因此,如果我们有很多域类,我们会有很多重复的代码,这反过来会使重构成为一场噩梦(见1.)。
  3. 只有在我们希望为所有域类添加某些属性时才有效,否则我们必须删除生成的代码,在我看来,这会破坏模板的目的
  4. 但是,对于我的所有域类添加@ToString注释的内容非常有用。