【问题标题】:inheritance in Grails domain modelGrails 领域模型中的继承
【发布时间】:2014-02-24 12:02:09
【问题描述】:

我的 Grails 应用的域模型有以下要求:

  • 一个用户属于零个或一个组织
  • 组织是慈善机构或公司
  • 慈善机构和公司有一些公共字段和一些(不可为空的)字段,它们对每个组织类型都是唯一的

我将公共组织字段放入一个抽象的Organisation 类中,CharityCompany 都扩展了该类。我无法将此层次结构存储在单个表中,因为每个组织类型都有特定的不可空字段。领域模型的相关部分如下所示:

class User {
  String name

  static belongsTo = [organization: Organization]

  static constraints = {
    organization nullable: true
  }
}

abstract class Organization {    
    String name

    static hasMany = [users: User]

    static mapping = {
        tablePerHierarchy false
    }
}

class Charity extends Organization {
  // charity-specific fields go here
} 

class Company extends Organization {
  // company-specific fields go here
}

当我查看从这个模型生成的 MySQL 模式时,organization-company 和 organization-charity 之间的继承关系似乎被完全忽略了。尽管有一个带有名称列的组织表,但它与公司或慈善机构都没有主外键关系

【问题讨论】:

  • Re (1) - 我刚刚用一个只有这四个域类的空应用程序尝试了这个,我得到了一个一对多(用户表有一个 organization_id bigint 是外键到组织表的id 列)。你用的是什么数据库?
  • @IanRoberts 我正在使用 MySQL
  • 我建议用 MySQL 创建你想要的数据库。然后,使用 Grails Database Reverse Engineering Plugin 将其导入 Grails
  • @chelder 我不认为这对我有用,因为这个插件无法知道charity:organisation 和 company:organisation 之间的映射应该是 1:1 而不是 1: N

标签: grails grails-orm


【解决方案1】:

ORM 不是 RDBS。

  1. tablePerHierarchy 错误

所以你有三个表:Organization、Charity、Company。用户仅属于组织(不属于慈善机构或公司)。你将如何获得特定领域的价值? 有用户。我们知道 ORGANIZATION,但我们不知道 Charity 或 Company。我想你明白...

我可以向您推荐三个解决方案:

1. tablePerHierarchy true(但您需要有可为空的charity\Company 特定字段)

2.

class User {
    static belongsTo = [charity: Charity, company: Company]
}


class Charity {
    String name
    static hasMany = [users: User]
    // charity-specific fields go here
}

class Company {
    String name
    static hasMany = [users: User]
    // company-specific fields go here
}  

3.

class User {
    static belongsTo = [organization: Organization]
}

class Organization {    
    String name
    Charity charity //nullable
    Company company //nullable
    static hasMany = [users: User]
}

class Charity {
    static belongsTo = [organization: Organization]
    // charity-specific fields go here
}

class Company {
    static belongsTo = [organization: Organization]
    // company-specific fields go here
}  

【讨论】:

  • 1. 我不能使用tablePerHierarchy,因为它需要我引入无效的nullable: true 约束2. 这会重复字段这是对所有类型组织的命令,这正是我试图避免的3。该提案与我在问题中提出的问题相同。换句话说,从Organization 中删除abstract 对生成的架构没有影响
  • 1.在这种情况下,我使用:nullable:true AND 验证器。例如:validator: { val, obj -> obj.isCompany()?值:真}
  • 1.在这种情况下,我使用:nullable:true AND 验证器。例如:validator: { val, obj -> obj.isCompany()? val : true } 2. 是的。它不是 DRY,但不是大问题。 3.删除摘要并删除“扩展组织”
  • 1.我想这会起作用,但它不是很漂亮 3. 抱歉,我没有注意到你已经用关联替换了继承继承。我认为hasOne 可能是比belongsTo 更好的映射
【解决方案2】:
  1. 对于 MySQL 和 H2,我看到的结果与 IanRoberts 相同。换句话说:没有生成连接表,但users 表中的预期organization_id FK。
  2. 使用“每个子类的表”映射 (tablePerHierarchy false),您最终会在数据库中获得隐含的一对一关系。 CharityCompany 的主键将具有与父 Organization 的 PK 相同的值。 GORM/Hibernate3 生成的模式似乎没有通过引用完整性约束来强制执行此操作。这是纯粹的休眠魔法。更详细一点here

【讨论】:

  • GORM/Hibernate3 生成的模式似乎没有通过参照完整性约束来强制执行此操作 - 这非常令人惊讶,但与我看到的行为一致
  • 是的,这也让我感到惊讶。最近添加的(未记录的?)tablePerConcreteClass 映射样式:jira.grails.org/browse/GRAILS-2759 是另一种可能满足或不满足您需求的选项
  • 我已经打开了一张票以将其添加到文档中:jira.grails.org/browse/GRAILS-11069
【解决方案3】:

解决了!

将下面的类添加到src/java(这个类不能用Groovy编写)

package org.example;

import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.MappingException;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;

import java.util.Iterator;

public class TablePerSubclassConfiguration extends GrailsAnnotationConfiguration {

    private static final long serialVersionUID = 1;

    private boolean alreadyProcessed = false;

    @Override
    protected void secondPassCompile() throws MappingException {
        super.secondPassCompile();

        if (alreadyProcessed) {
            return;
        }

        for (PersistentClass persistentClass : classes.values()) {
            if (persistentClass instanceof RootClass) {
                RootClass rootClass = (RootClass) persistentClass;

                if (rootClass.hasSubclasses()) {
                    Iterator subclasses = rootClass.getSubclassIterator();

                    while (subclasses.hasNext()) {

                        Object subclass = subclasses.next();

                        // This test ensures that foreign keys will only be created for subclasses that are
                        // mapped using "table per subclass"
                        if (subclass instanceof JoinedSubclass) {
                            JoinedSubclass joinedSubclass = (JoinedSubclass) subclass;
                            joinedSubclass.createForeignKey();
                        }
                    }
                }
            }
        }

        alreadyProcessed = true;
    }
}

然后在DataSource.groovy中设置这个为配置类

dataSource {
    configClass = 'org.example.TablePerSubclassConfiguration'
    pooled = true
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
    dbCreate = "update"
    url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}

我已向 Grails 提交了 pull request 来解决此问题。该修复已包含在 Grails 2.3.9 中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多