在关系数据库中实现继承

在关系数据库中实现继承
在将对象保存到关系数据库中时,继承的概念中发生几个有趣的问题。(请参阅参考资料中的 "Building Object Applications That Work"。)问题从根本上归结为解释如何在您的持久模型中组织继承的属性。解决这个难题所用的方法会对系统设计有很大影响。将继承映射到关系数据库中有三种基本解决办法,为更好地理解它们,我将讨论在图 1 中显示的映射类图表的优缺点。为简化问题,我没有为类的所有属性都建模;也没有为其完整签名或任何类方法建模。

图 1. 简单类层次结构的 UML 类示意图
图 1. 简单类层次结构的 UML 类示意图

将类映射成表
类到表的映射通常不是直接的。除了非常简单的数据库以外,您不会有类到表的一对一映射。在以下章节中,我将讨论为关系数据库实现继承结构的三种策略:



整个类层次结构使用一个数据实体
使用这种方法,您可以将一个完整类层次结构映射成一个数据实体,而层次结构中所有类的所有属性都存储在这个实体中。图 2 描述了采取这个方法时图 1 的类层次结构的持久模型。请注意,为表的主键引入了一个 personOID 列 - 我在所有解决方案中都使用 OID (没有商业含义的标识,又称替代键),只是为了保持一致和使用我所知道的向数据实体分配键的最好办法。

图 2. 将类层次结构映射成单一数据实体
图 2. 将类层次结构映射为单一数据实体

这种方法的优点是简单,因为所需的所有人员数据都可以在一张表中找到,所以在人们更改角色时支持多态性,并且使用这种方法,专门报告(为一小组用户特定目的所执行的报告,这些用户通常自己写报告)也非常简单。缺点是每次在类层次结构的任何地方添加一个新属性时都必须将一个新属性添加到表中。这增加了类层次结构中的耦合 - 如果在添加一个属性时有任何错误,除获得新属性的类的子类外,还可能影响到层次结构中的所有类。它还可能浪费数据库中的许多空间。我还必须添加 objectType 列来表明行代表的是学生、教授还是其它类型的人员。在人们具有单一角色时这种方法很有效,但如果他们有多个角色(例如,一个人既是学生又是教授),很快就会失效。

每个具体类使用一个数据实体
使用这种方法,每个数据实体就既包含属性又包含它所表示的类继承的属性。图 3 描述了采取这个方法时图 1 的类层次结构的持久模型。有与 Student 类对应的和与 Professor 类对应的数据实体,因为它们是具体类,但没有与 Person 类对应的数据实体,因为它是抽象类(它的名称以斜体字表示)。为每个数据实体都分别分配了自己的主键, studentOIDprofessorOID

图 3. 将每个具体类映射成单个数据实体
图 3. 将每个具体类映射成单个数据实体

这种方法最大的好处是,它仍然能相当容易地执行专门报告,只要您所需的有关单一类的所有数据都只存储在一张表中。但也有几个缺点。一个是当修改类时,必须修改它的表和它所有子类的表。例如,如果要向 Person 类添加高度和重量,就需要同时更新两个表,它会涉及很多工作。第二,无论何时,只要对象更改了它的角色 - 可能您聘用了您一个刚毕业的学生作为教授 - 则需要将数据复制到相应的表中,并为它指定一个新的 OID。这又涉及到很多工作。第三,很难在支持多个角色的同时仍维护数据完整性。(这种情况是可能的;只是比原先困难一点。)例如,您会在哪里存储既是学生又是教授的人的姓名呢?

每个类使用一个数据实体
使用这种方法,为每个类创建一张表,它的属性是 OID 和特定于该类的属性。图 4 描述了采取这个方法时图 1 的类层次结构的持久模型。 请注意,将 personOID 用作了所有三个数据实体的主键。图 4 的一个有趣的特性是,为 ProfessorStudent 中的 personOID 列都分配了两个构造型,而这在标准建模语言 (UML) 中是不允许的。我的意见是,这是一个必须由 UML 持久性建模概要解决的问题,甚至可能在这个建模规则中也需要更改。(有关持久性模型的详细信息,请参阅参考资料中的 "Towards a UML Profile for a Relational Persistence Model"。)

图 4. 将每个类映射成它自己的数据实体
图 4. 将每个类映射成它自己的数据实体

这种方法的最大好处就是它能够最好地适应面向对象的概念。它能够很好地支持多态性,对于对象可能有的每个角色,只需要在相应的表中保存记录。修改超类和添加新的子类也非常容易,因为您只需要修改或添加一张表。这种方法也有几个缺点。第一,数据库中有大量的表 -- 实际上每类都有一个(加上维护关系的表)。第二,使用这种技术读取和写入数据的时间比较长,因为您必须访问多个表。如果通过将类层次结构中的每个表放入不同物理磁盘驱动器盘片(假设每个磁盘驱动器磁头都单独操作)上来智能地组织数据库的话,就可以缓解这个问题。第三,有关数据库的专门报告很困难,除非添加一些视图来模拟所需的表。

比较映射策略
现在,请注意,每个映射策略怎样产生不同的模型。要理解三种策略之间的设计优缺点,请考虑图 5 中显示的对我们的类层次结构做些简单的更改:添加了 TenuredProfessor,这是从 Professor 中继承的。

图 5. 扩展初始类层次结构
图 5. 扩展初始类层次结构

图 6 显示了一个更新过的持久性模型,用于将整个类层次结构映射成一个数据实体。尽管很明显,数据库中的空间浪费增加了,但请注意,按照这种策略操作,只需花非常小的代价就可以更新模型。

图 6. 将扩展的层次结构映射成单一数据实体
图 6. 将扩展的层次结构映射成单一数据实体

图 7 显示了将每个具体类映射成数据实体时的持久性模型。使用这个策略,虽然因为我们从教授提升到终身教授,这样对象和我们的关系就有了改变(学生变成教授),所以如何处理对象的这个问题更复杂了,但我只需要添加一个新表。

图 7. 将扩展的层次结构的具体类映射成数据实体
图 7. 将扩展的层次结构的具体类映射成数据实体

图 8 显示了第三种映射策略的解决方案 -- 将单个类映射成单个数据实体。这需要我添加一个只包括 TenuredProfessor 类的新属性的新表。这种方法的缺点是,要使用新类的实例,它需要好几个数据库访问。

图 8. 将扩展的层次结构的所有类映射成数据实体
图 8. 将扩展的层次结构的所有类映射成数据实体

要摒弃这样一种观点,即这些办法都不够好;每种办法都有其优缺点。在下面的表 1 中对它们进行比较。

表 1. 比较映射继承的各种办法

考虑因素
每个层次结构一张表
每个具体类一张表
每个类一张表
专门报告
容易
中等
中等/困难
实现的难易程度
容易
中等
困难
数据访问的难易程度
容易
容易
中等/容易
耦合
非常高
数据访问速度
中等/快
对多态性的支持
中等

映射关联、聚合和组成
不仅必须将对象映射到数据库中,还必须将对象之间的关系进行映射,这样才能在以后进行恢复。对象之间有四种类型的关系:继承、关联、聚合和组成。要有效地映射这些关系,必须理解它们之间的差异、如何实现一般的关系,以及如何实现特定的多对多关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值