对象-关系数据库之间的映射1
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
对象-关系数据库之间的映射
为什么对象-关系数据库的映射对于现代开发者是一件大事呢?一方面,对象技术(例如 Java 技术)是应用于新软件系统开发的最常见的环境。另外,关系数据库仍然是许多人都青睐的持久信息存储方法,并且在较长时间内这种情况不太会改变。请继续读下去,了解如何使用这种技术。
为什么要写有关对象-关系数据库之间的映射的文章呢?因为在对象范例和关系范例之间“阻抗不匹配”。对象范例基于软件工程的一些原理,例如耦合、聚合和封装,而关系范例则基于数学原理,特别是集合论的原理。两种不同的理论基础导致各自有不同的优缺点。而且,对象范例侧重于从包含数据和行为的对象中构建应用程序,而关系范例则主要针对数据的存储。当为访问而寻找一种合适的方法时,“阻抗不匹配”就成了主要矛盾:使用对象范例,您是通过它们的关系来访问对象,而使用关系范例,则通过复制数据来联接表中的行。这种基本的差异导致两种范例的结合并不理想,不过话说回来,本来就预料到会有一些问题。使对象-关系数据库之间的映射成功的一个秘诀就是理解这两种范例和它们的差异,然后基于这些认识来进行明智的取舍。
本文应该能够消除现今开发周期中一些普遍共有的误解,对对象-关系数据库之间映射所涉及到的一些问题提供了切合实际的看法。这些策略基于我的开发经验,项目范围从小到大,涉及金融、销售、军事、远程通信和外购等行业。我已对使用 C++、 Smalltalk、Visual Basic 和 Java 语言编写的应用程序应用了这些原则。
如何将对象映射成关系数据库
在这一节中,我会描述一些将对象成功映射成关系数据库所需的基本技术。
∙将属性映射成列
∙在关系数据库中实现继承
∙将类映射成表
∙映射关联、聚合和组合
∙实现关系
将属性映射成列
类属性将映射成关系数据库中的零或几列。要记住,并不是所有属性都是持久的。例如,
Invoice类会有grandTotal属性,这个属性由其实例在计算时使用,但它不保存到数据库中。而且,某些对象属性本身就是对象,例如Course对象有一个作为属性的TextBook实例,它映射为数据库中的几列(实际上,很有可能TextBook类本身就将映射成一个或多个表)。重要的是,这是一个递归定义:有时属性将映射成零或者多列。也有可能将几个属性映射成表中的单一列。例如,代表美国邮递区号代码的类可以有三个数字属性,每个都表示完整邮政编号代码中的每一部分,而邮政编号代码可以在地址表中作为单一的列存储。
在关系数据库中实现继承
在将对象保存到关系数据库中时,继承的概念中发生几个有趣的问题。(请参阅参考资料中的"Building Object Applications That Work"。)问题从根本上归结为解释如何在您的持久模型中组织继承的属性。解决这个难题所用的方法会对系统设计有很大影响。将继承映射到关系数据库中有三种基本解决办法,为更好地理解它们,我将讨论在图 1 中显示的映射类图表的优缺点。为简化问题,我没有为类的所有属性都建模;也没有为其完整签名或任何类方法建模。
图 1. 简单类层次结构的 UML 类示意图
将类映射成表
类到表的映射通常不是直接的。除了非常简单的数据库以外,您不会有类到表的一对一映射。在以下章节中,我将讨论为关系数据库实现继承结构的三种策略:
∙整个类层次结构使用一个数据实体
∙每个具体类使用一个数据实体
∙每个类使用一个数据实体
整个类层次结构使用一个数据实体
使用这种方法,您可以将一个完整类层次结构映射成一个数据实体,而层次结构中所有类的所有属性都存储在这个实体中。图 2 描述了采取这个方法时图 1 的类层次结构的持久模型。请注意,为表的主键引入了一个personOID列 - 我在所有解决方案中都使用 OID (没有商业含义的标识,又称替代键),只是为了保持一致和使用我所知道的向数据实体分配键的最好办法。
图 2. 将类层次结构映射成单一数据实体
这种方法的优点是简单,因为所需的所有人员数据都可以在一张表中找到,所以在人们更改角色时支持多态性,并且使用这种方法,专门报告(为一小组用户特定目的所执行的报告,这些用户通常自己写报告)也非常简单。缺点是每次在类层次结构的任何地方添加一个新属性时都必须将一个新属性添加到表中。这增加了类层次结构中的耦合 - 如果在添加一个属性时有任何错误,除获得新属性的类的子类外,还可能影响到层次结构中的所有类。它还可能浪费数据库中的许多空间。我还必须添加objectType列来表明行代表的是学生、教授还是其它类型的人员。在人们具有单一角色时这种方法很有效,但如果他们有多个角色(例如,一个人既是学生又是教授),很快就会失效。
每个具体类使用一个数据实体
使用这种方法,每个数据实体就既包含属性又包含它所表示的类继承的属性。图 3 描述了采取这个方法时图 1 的类层次结构的持久模型。有与Student类对应的和与Professor类对应的数据实体,因为它们是具体类,但没有与Person类对应的数据实体,因为它是抽象类(它的名称以斜体字表示)。为每个数据实体都分别分配了自己的主键,studentOID和professorOID。
图 3. 将每个具体类映射成单个数据实体
这种方法最大的好处是,它仍然能相当容易地执行专门报告,只要您所需的有关单一类的所有数据都只存储在一张表中。但也有几个缺点。一个是当修改类时,必须修改它的表和它所有子类的表。例如,如果要向Person类添加高度和重量,就需要同时更新两个表,它会涉及很多工作。第二,无论何时,只要对象更改了它的角色 - 可能您聘用了您一个刚毕业的学生作为教授 - 则需要将数据复制到相应的表中,并为它指定一个新的 OID。这又涉及到很多工作。第三,很难在支持多个角色的同时仍维护数据完整性。(这种情况是可能的;只是比原先困难一点。)例如,您会在哪里存储既是学生又是教授的人的姓名呢?
每个类使用一个数据实体
使用这种方法,为每个类创建一张表,它的属性是 OID 和特定于该类的属性。图 4 描述了采取这个方法时图 1 的类层次结构的持久模型。请注意,将personOID用作了所有三个数据实体的主键。图 4 的一个有趣的特性是,为Professor和Student中的personOID列都分配了两个构造型,而这在标准建模语言 (UML) 中是不允许的。我的意见是,这是一个必须由UML 持久性建模概要解决的问题,甚至可能在这个建模规则中也需要更改。(有关持久性模型的详细信息,请参阅参考资料中的 "Towards a UML Profile for a Relational Persistence Model"。)
图 4. 将每个类映射成它自己的数据实体