知识卡片

具体表继承的键唯一性噩梦,与超类域的绕过技巧

普通读书笔记卡

内容

具体表继承给继承层次里每个具体类各建一张表,超类的域会在每个子类表里被复制一份——这带来一个格外棘手的键问题:键不仅要在单张表里唯一,还必须在继承层次涉及的所有表之间保持全局唯一(比如足球运动员表和板球运动员表都用标识域当键,如果两张表能出现相同的键值,同一个键值查出来的就可能是两行不同的数据),因此不能依赖数据库自身的主键唯一性机制来保证这一点,而需要一个专门的键分配系统去跨表记录键的使用情况;如果要接入一个已经被其他系统使用、无法保证跨表键唯一的既有数据库,情况会更糟,此时要么干脆放弃在子类表里保留超类域,要么改用包含表名信息的组合键。放弃保留超类域会连累对象模型本身,一种更巧妙的绕过技巧是:在公共接口(超类)上提供访问器方法,但具体实现上给每个具体类型各自的私有域,由接口方法在运行时把这些散落各处的私有域值组合起来对外呈现——如果接口该返回单值,就从那些非空的私有值里挑一个;如果接口该返回集合,就把各实现域的值联合起来一并返回。除此之外,跨表引用完整性也是这个模式的软肋:想给”运动员”这种没有对应表的抽象概念建外键约束根本无从下手,只能选择放弃引用完整性检查,或者对每个实际存在的子类表各建一张对应的链接表。可迁移启发:给继承层次选具体表继承时,真正的隐藏成本往往不在建表本身,而在于”键的全局唯一性”和”跨子类的引用完整性”这两件事——一旦决定采用这种方案,这两个问题几乎无法回避,需要提前规划专门的机制来兜底。

参考来源

- 位置:《企业应用架构模式》第二部分"模式"之"第12章 对象-关系结构模式"之"12.9.1 运行机制"(源文件:_epub-src/OEBPS/Text/000138.html) - 结论依据:原文说明"重要的是保证键不仅在一个表中是唯一的,而且在继承层次里所有的表中都是唯一的……因此,需要一个键分配系统来记录表间键的使用情况;而且,不能依赖数据库的主键唯一机制……另一种方法是在接口中为超类型建立一些访问器,但是在实现中为每个具体类型使用私有的域。随后,接口就组合来自几个私有域的值",直接支撑本卡结论。 - 原始内容:问题在于并没有表与运动员对应,所以你不能为运动员的外键域创造一个引用完整性约束,因为外键域可以是足球运动员的,也可以是板球运动员的。这种情况下要么忽略引用完整性,要么使用多链接表。