是的,当然可以这样做。
这是对象关系映射(ORM)框架工作的方式之一(我认为是最好的方式)。这不是巧合,因为您要在此处实现的是映射到关系数据库的面向对象的继承层次结构。 “设备”是基类,而“灯”和“加热器”是派生(后代)类。
这里是怎么做:
摆脱Type
列。正如我将进一步展示的那样,它将成为一个计算列。
代替Type
列,引入几个所谓的“ descendant-id”列:HeaterId
列和LampId
列。如果希望的话,添加一个约束,即仅允许那些后代ID列之一为非空。而且,如果以后添加更多设备,则必须添加更多后代ID列。
然后,您可以使Type
成为计算列,基于该列之一,后代ID列包含非NULL值。
坏消息是您将无法设置Type
列的值,但这没关系;而不是设置Type
,而是通过将非null值存储到子代ID列之一中来隐式定义类型。
注意:在面向对象的程序设计中,很少设计基类,以使其对其后代有任何了解。甚至有人说拥有这种知识是荒谬的。我不是绝对的,我遇到了一些小型的,紧密编织的,不可扩展的类图的例子,这些知识是合法且有用的。但是无论如何,即使您严格遵守这样的格言:基类永远不应该了解其子代,这也不是完全面向对象的程序设计:这是关系数据库设计。这是将类图映射到数据库时必须支付的价格。
进一步的澄清:
首先,使用一些术语:“长行”是一对父行(基础行/祖先行)加上后代行,一起检查。 “行片段”只是其中的一个,单独进行检查。
每个后代行片段必须具有与其父行片段完全相同的主键ID。因此,这是一个示例结构:
Device table:
+-----+----------+----------+
| Id | LampId | HeaterId |
+-----+----------+----------+
| 1 | NULL | 1 |
| 2 | 2 | NULL |
| 3 | 3 | NULL |
| 4 | NULL | 4 |
+-----+----------+----------+
Lamp table:
+-----+----
| Id |
+-----+----
| 2 |
| 3 |
+-----+----
Heater table:
+-----+----
| Id |
+-----+----
| 1 |
| 4 |
+-----+----
这意味着在基本行片段中,一个非空的后代ID始终具有与同一行的主键完全相同的值。
这样,除了非常容易进行故障排除之外,还具有性能上的额外好处:将整个长行插入数据库不需要对数据库进行任何额外的往返操作:您可以使用序列或自动递增值来仅发出一个ID,这是参与插入的所有行片段所需的唯一ID。即使您的继承层次结构深于两个层次,这也像一个魅力一样起作用。