知识卡片

三种继承映射策略的权衡

结构图卡

内容

SQL没有标准的继承表达方式,因此把一个继承类层次映射到关系表,一般有三种选择,本质是在”数据结构复制”和”访问速度”之间做权衡。单表继承:整个层次的所有类共用一张表——最大的好处是把所有内容放在一起,修改容易、还避免了连接操作;主要弊端是每一行都要为所有可能的子类预留列,造成大量空列浪费空间(多数数据库能较好压缩这种浪费),而且表本身的体积会逐渐成为访问瓶颈。类表继承:层次中每个类各自建一张表——这是类和表之间最简单直观的对应关系,但载入一个对象往往需要多次连接操作,通常会拖累性能。具体表继承:每个具体类各自建一张表——避免了连接操作、可以从一张表直接取出一个完整对象,但可维护性差:修改超类会牵连所有子类对应的表和映射代码,改动层次结构本身代价更大;同时因为没有独立的超类表,主键管理会很棘手、引用完整性也难保证(好处是减少了对超类表的锁争夺)。三种选择并不互斥,同一个层次里可以混合使用(比如整体用单表继承、个别特殊情况单独用类表继承处理),但混用会增加复杂性。作者本人没有给出绝对最优解,个人倾向单表继承(易实现、易重构),需要时再用另外两种模式解决不可避免的空列/无用列问题,并建议最终结合具体环境和数据库管理员的建议决定。多继承/接口的情况本质上是这三种模式的变体:单表继承把所有超类和接口塞进一张大表,类表继承为每个接口和超类各建一张独立表,具体表继承在每张具体表里包含所有接口和超类的列。

结构图

flowchart TB
  A["继承映射三选择"]
  A --> B["单表继承<br/>一张表装整个层次"]
  A --> C["类表继承<br/>每个类一张表"]
  A --> D["具体表继承<br/>每个具体类一张表"]
  B --> B1["优:免连接/易改<br/>缺:空列浪费/表体积成瓶颈"]
  C --> C1["优:结构最直观<br/>缺:多连接拖累性能"]
  D --> D1["优:免连接/单表取完整对象<br/>缺:改超类牵连所有表/主键管理难"]

参考来源

- 位置:《企业应用架构模式》第一部分"表述"之"第3章 映射到关系数据库"之"3.4.2 继承"(源文件:_epub-src/OEBPS/Text/000021.html) - 结论依据:原文说明"在数据结构复制和访问速度之间必须进行权衡。类表继承是类和表之间最简单的关系,但是它需要多个连接(join)操作来载入一个对象……具体表继承避免了连接操作……但是改变起来比较困难……单表继承最大的弊端是浪费了空间……它最大的好处是把所有的内容都放到一起,这样修改起来很容易并且避免了连接操作",直接支撑本卡结构图。 - 原始内容:类表继承是类和表之间最简单的关系,但是它需要多个连接(join)操作来载入一个对象,这样通常损失了性能。具体表继承避免了连接操作,允许从一个表中取得一个对象,但是改变起来比较困难。