【问题标题】:Table design and class hierarchies表设计和类层次结构
【发布时间】:2011-05-07 18:02:21
【问题描述】:

希望有人可以通过示例或一些建议阅读来阐明这个问题。 我想知道在类层次结构等效之后对表建模的最佳设计方法是什么。这可以通过一个例子来最好地描述:

abstract class Card{
    private $_name = '';
    private $_text = '';
}

class MtgCard extends Card{
    private $_manaCost = '';
    private $_power = 0;
    private $_toughness = 0;
    private $_loyalty = 0;
}

class PokemonCard extends Card{
    private $_energyType = '';
    private $_hp = 0;
    private $_retreatCost = 0;
}

现在,当对表进行建模以与此类层次结构同步时,我采用了非常相似的方法:

TABLE Card
  id            INT, AUTO_INCREMENT, PK
  name          VARCHAR(255)
  text          TEXT

TABLE MtgCard
  id            INT, AUTO_INCREMENT, PK
  card_id       INT, FK(card.id)
  manacost      VARCHAR(32)
  power         INT
  toughness     INT
  loyalty       INT

TABLE PokemonCard
  id            INT, AUTO_INCREMENT, PK
  card_id       INT, FK(card.id)
  hp            INT
  energytype    ENUM(...)
  retreatcost   INT

我遇到的问题是试图弄清楚如何将每个Card 记录与相应表中包含其详细信息的记录相关联。具体来说,如何确定我应该查看哪个表。

我应该在Card 中添加一个VARCHAR 列来保存关联表的名称吗?这是我和我的同龄人达成的唯一解决方案,但它似乎太“肮脏”了。 保持设计的可扩展性是这里的关键,允许轻松添加新的子类

如果有人可以提供一个示例或资源,展示一种清晰的镜像类/表层次结构的方式,将不胜感激。

【问题讨论】:

  • 你看过 (N)Hibernate 是做什么的吗?它可能会给你一些关于不同可能方法的想法。
  • @Philipp:没有,但我在其他主题的主题中看到过它。我一定会调查的。

标签: language-agnostic database-design orm class-hierarchy


【解决方案1】:

谷歌“泛化专业化关系建模”。您将找到几篇关于如何使用关系表对 gen-spec 模式建模的优秀文章。在 SO 中已经多次提出同样的问题,但细节略有不同。

最好的这些文章将确认您的决定,即使用一个表来存储通用数据,而使用单独的表来存储专业数据。最大的不同是他们推荐使用主键和外键的方式。基本上,他们建议专用表有一个具有双重职责的列。它作为专用表的主键,但它也是复制通用表的 PK 的外键。

这维护起来有点复杂,但在加入时非常甜蜜。

还请记住,将新类添加到层次结构时需要 DDL。

【讨论】:

  • 您能否按照您描述的方式提供专用表中主键的示例或参考?
  • @orange,好评。我将不得不查看这些文章,看看其中一篇是否能很好地说明这一点。
  • 谢谢沃尔特·米蒂;有一些很好的资源可以克服对象关系阻抗不匹配,包括另一个 SO 线程 (stackoverflow.com/questions/1567935/…)
  • 感谢 Fowler 网站的指点。这是好东西。
【解决方案2】:

基本上不会。

忘记类层次结构、存储模型以及任何特定于您的应用和特定应用语言的东西。除非您只想将 RDb 用作文件的存储位置,否则就是依赖从属。

如果您想要关系数据库的强大功能和灵活性(特别是可扩展性),那么您需要独立于任何应用程序对其进行建模,并使用 RDb 原则,而不是应用程序语言要求。将您的应用程序上下文暂时搁置一会,并将数据库设计为数据库。了解他们。规范化(消除所有重复)。了解结构和规则,并实施它们。当您这样做时,您的查询和“映射”将毫不费力。不会有“阻抗”。使用正确的数据类型,不会出现不匹配的情况。

你需要的结构是一个普通的子类型-超类型。这些是关系数据库术语,在 RM 中已经存在了 30 多年,在关系数据库产品中已经存在了 23 多年。无需称它们为有趣的新名称。维基百科不是学术参考。

鉴于您的表格作为起点非常正确(您已自动标准化),您需要:

  • 将 Card.Id 重命名为 Card.CardId

  • 删除子类型的 id,它们是 100% 冗余的; CardId 既是 PK 也是 FK。

  • 添加鉴别器 Card.CardType CHAR(1) 或 TINYINT。当 CardType 未知时,这将确定要加入的子类型。

  • 看来您还没有完全理解外键的概念,所以最好先做好准备。它在这里以简单、普通的形式实现:

    ALTER TABLE MtgCard
        ADD CONSTRAINT Card_MtgCard_fk
        FOREIGN KEY (CardId)
        REFERENCES Card(CardId)
  • Card 与 MtgCard 或 PokemonCard 之间的关系始终为 1::1。只有当有卡片加 { MtgCard | 时,超类型才完整。 PokemonCard } 具有相同的 CardId。在您的情况下,只能有一个子类型,可以通过简单的 CHECK 约束轻松实施。

    • other cases 中,多个子类型是完全合法的。

    • 子类型有 Person Is a TeacherPerson Is a Student

  • 在关系数据库中没有连接“从”或“到”(或上/下或左/右)的概念,这些概念只是为了帮助我们人类;您可以从您拥有的任何表/键开始,然后转到您需要的任何表。只有在没有关系标识符的情况下才需要中间的表(即,其中 additional 代理、ID 列被用作 PK而不是有意义的自然键)。

    • 在示例中,使用您的条款,您可以直接注册人(例如,获取姓氏)或to 课程(获取名称)而无需访问中间表;关系线是实心的。
      .
  • 现在,类层次结构(“Is”或“Is a”)和其他任何东西都变得简单而轻松。

Quick Reference 转换为标准关系数据库图。

【讨论】:

    猜你喜欢
    • 2014-12-06
    • 2013-11-21
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    相关资源
    最近更新 更多