面向对象的数据库编程 O/R Mapping

来源:互联网 发布:微信跳转淘宝店铺 编辑:程序博客网 时间:2024/05/16 23:52

1.2.1   什么是O/R Mapping
如果我们采用面向对象的思想进行系统的分析设计,那么数据库采用关系型数据库就会存在对象模型和关系模型两者之间“不匹配”这个不容忽视的问题。面向对象设计和关系型数据库设计存在很大的不同。对象模型基于软件工程的一些原理,面向对象设计的理论包括封装、关联、聚合、继承、多态,而关系模型主要针对数据的存储。我们希望通过面向对象的设计方式来完成业务流程,这一点可以实现,但最终如何把对象保存下来是必须考虑的。如果我们的对象持久化存贮介质采用关系型数据库,那么怎样将对象保存到关系型数据库的时候矛盾也就出现了。因此,我们要把对象映射到关系数据库,通过O/R Mapping的技术手段来架起对象和关系型数据库的桥梁。显然,O/R Mapping主要解决对象层次的映射、对象关系的映射以及对象的持久化问题。
O/R Mapping作为面向对象数据库编程中的重要术语,由Martin在其2003年出版的重要著作《Agile Software Development:Principles、Pattens、and Practices》中提到。其所谓的“对象/关系映射”指的是应用程序中使用到的生命短暂的对象与数据库中可以持久保存的记录之间所建立的一种一致性关系,这种关系使得应用程序对关系数据库中的存储、访问操作简化为对实体对象的操作,同时隐匿了数据持久化工作的复杂性。这种映射关系可以是双向的。O/R Mapping通常有以下几种形式:
· 把概念上的类正向映射到数据库的表。
· 把数据库的表反向映射到概念上的类。
· 把设计好的类正向映射到数据库的表。因为设计好的类已经具有了属性和操作,因此映射这种映射不是静态的,它也需要考虑数据库表的活动的方面,例如:存储过程和触发器。
· 把数据库的表反向映射到设计好的类。这种形式的映射需要解决数据库存储过程和触发器活动时对应用程序的影响。
· 在程序内存空间中把表中的记录缓存为类的对象。这种情况多使用的运行期,而且要考虑数据库的事务处理,返回数据库提交或会滚的结果。
所谓概念上的类是指那些只包含属性数据而忽略其操作方法的类,这些类通常作为一个数据容器,类似记录或数组。这使得其与数据库的表之间更容易映射。因此,这类O/R Mapping的挑战来自于类与表之间的关系处理,例如:继承、多重性(如:一对多或多对多等对应关系)
O/R Mapping是面向对象分析设计的产物,也是分层设计要解决的问题之一。O/R Mapping虽然有一定技术上的难度,但其给程序设计带来的好处却是不言而喻的。在面向对象的分层设计的系统体系中,上层的程序执行最终结果都是要操作数据库,而数据库是关系型,不是面向对象的,正是通过对象关系的映射,使我们实现了只对上层对象的操作实现对表的操作,感觉好象没有数据库的存在,上层只管面向对象编程就可以了。这就好比示例程序 13‑1所示的代码中,我们可以用Delphi面向对象的代码完全取代对应的SQL代码。


1.2.2   把对象映射到关系数据库
要克服O/R阻抗不匹配,我们既要理解将对象映射到关系数据库的过程,又要理解如何实现这些映射。我们先来了解O/R Mapping的基本知识。
通常在系统中,需要映射到数据库中的是需要持久化的实体类(或业务类),而不是哪些起控制、运算功能的类。这些类的特点是它的属性(attribute)多而操作(operation)少。
类的属性映射对应成关系数据库表中的字段。如图 13‑1所示,上图是一张UML类图,下图是映射的数据表的实体关系图(ER图)。为了简单明了,在每个模型中,这里都使用了有规律的命名规范,类的属性名映射到了表成为字段名。
对象的有些属性本身就是对象,如图 13‑1所示的类模型中,TBook的cover属性实际上是一个TCover的对象,它真实反映了两个类之间的合成关系,因此在影射时,需要处理对象之间的关系。O/R Mapping不仅要把对象映射到数据库中,还要把对象的关系映射到数据库中。因此,为了更好实现O/R Mapping,我们先要搞清对象之间的几种不同的关系,以便根据不同的关系来进行相应的映射。
正如本书前面专门讨论的对象关系所述,对象关系包括继承与关联关系,关联关系包括了一对一、一对多和多对多等类型的多重性。要有效地映射这些关系,必须理解它们之间的差异,我们将分以下两类讨论如何实现O/R Mapping。
· 继承(Inheritance)  继承也称泛化关系,是类与类之间最基本的关系,后面我们将讨论如何影射继承结构。
· 关联(Associations)  关联关系是类与类之间的连接,它使一个类指到另一个类的属性和方法。后面我们将讨论如何影射关系。
需要注意的是,即使是在需要持久化的实体类中,也非所有的属性都是持久的,一些属性被用来进行临时计算,一些属性可以从其他属性派生或推算出来,他们可能都没有保存到数据库中的必要,因此影射时无需对应到表中的字段。相反,对象需要维护的任何用于将它们自己持久化的数据都应该映射到表中的对应字段,尽管这些属性只是一些“影子信息”对于普通业务并不其作用。这一类属性一般会包括主键信息、并发控制标记、时间戳、增量计数器等。
只有当我们可以控制对象和数据库模式(schema)时,映射才是完美的。在一些情形下,我们不能很容易改变遗留系统的数据库模式,实际工作中会发现映射相当复杂。在这些情形下,类和表间的映射变成多对多的,即类的属性会映射到多个表的字段上,给定的表也可能映射到多个类上,这时最好使用O/R Mapping工具或编写专门的代码来封装这些复杂的映射,后面我们介绍如何使用ECO来高效完成这样复杂的工作。
O/R Mapping通常的做法是将程序中的类映射到数据库的一个或多个表,但是这种方式会带来一些问题。首先就是数据实体在数据库和程序中的表现方式不一样,对于一些涉及到多个表的“粗粒度对象”,一个实体类可能会引用到多个其它实体类,也就是说会在涉及到对象粒度的建模方面带来一些问题;其次在同数据库交互时,也涉及到一个转换的问题,如果一个对象涉及到对多个表的操作,问题就更大;最后,当系统做查询操作,需要返回多个对象时,因为涉及到转换的问题,效率就比较低下。
上面提到的这些都是我们实际要考虑的问题,这里就涉及到做O/R Mapping的原则问题。我们做数据映射时依据一个什么样的标准,以什么原则为指导,那些因素是我们在映射的过程中必须考虑的。
首先是性能。
对象映射到表时,性能将是最主要的考虑因素。对象映射到表的方式将会对数据库访问的次数有显著的影响。数据库访问通常是在磁盘上或其它外部媒介上执行,它们的访问时间数量级大约在毫秒级,而CPU的处理周期是在纳秒级。因此,较好的做法是浪费一些存储周期和内存空间,来提高慢速I/O的性能。其中有一些做法是计算机可以自己做的,例如Cache方式等等,有一些则需要我们通过编程来手动处理,减少对数据库的访问次数,例如延迟读、为数据置标志 位等等。
延迟读的核心思想是用到那些数据就读那些数据,比如一个图书对象,有个封面图片对象属性,图书对象与封面对象是一对一关系。做映射后,这两个对象虽然存放在同一个表中,在初始化图书这个对象时就没必要把封面这个对象一起初始化,当需要封面这个属性时再从数据库中读出来,给这个属性赋值。
其次,还要考虑空间消耗的问题。
不同的映射方式会使数据的存取空间有很大的不同,某些映射不会浪费数据库空间(类似于空值字段),另一些则需要大量无用的数据库记录。空间的消耗将会提高性能。我们是牺牲空间还是要提高性能,可能要因事因时而议,要作到最好的权衡。
最后是权衡数据库的可维护性和性能。
数据库的可维护性和性能是相互冲突的。在优化数据模型性能的同时,改变应用的维护成本也在上升。也需要我们权衡取舍。