理解O/R Mapping

本文的目的是以最精炼的语言,理解什么是O/R Mapping,为什么要O/R Mapping,和如何进行O/R Mapping。

什么是O/R Mapping?

广义上,ORM指的是面向对象的对象模型和关系型数据库的数据结构之间的相互转换。

狭义上,ORM可以被认为是,基于关系型数据库的数据存储,实现一个虚拟的面向对象的数据访问接口。理想情况下,基于这样一个面向对象的接口,持久化一个OO对象应该不需要要了解任何关系型数据库存储数据的实现细节。

为什么需要O/R Mapping?

广义上,因为我们需要面向对象来描述我们的业务,我们也需要关系型数据库来存储我们的数据。

有人可能会提到,我们未必要用面向对象来描述业务,或者未必用关系型数据库来存储数据。没错,但是,至少,还是有相当大部分的程序,同时需要这两者的合作。存在即合理。因为同时需要彼此,两者都客观存在,就值得讨论。

既然从广义上,存在即合理,无需讨论为什么需要ORM,很多关于ORM的讨论,其实都是针对上面提到的狭义的定义。但是即使到目前为止,真正能够完美的实现这个狭义定义的ORM的工具,其实还并不存在(很多工具如Hibernate,已经相当接近,但是,离完美还有相当距离)。

既然还不存在,那么,在讨论需不需要之前,我们恐怕要先讨论一下,是否可能,即从理论上,从数学上,面向对象的对象模型和关系型数据库的数据结构之间,到底能不能做到完美的映射?要回答这个问题,我们要解决两个难题。

O/R阻抗失衡

第一个难题,叫“O/R阻抗失衡(O/R Impedance
Mismatch)”,指的是OO对象模型和关系型数据库的数据结构之间的,设计理念上的差异。OO的设计理念,是以最正确的语义来描述真实世界;而关系型数据库的设计理念,则是从数学的角度,如何更有效的存储和管理数据。由于两者设计理念的差异,导致它们尽管从数据结构上,可能很相近,但是关注点往往是不同的。

例如:

  • 从OO的角度,凡是语义明确,每一个对象语义上的属性,就应该定义为一个属性;从关系型数据库的角度,有可能考虑到一些属性从来不会被作为查询条件,而把多个语义上的属性,以一定的格式,存在一个数据表的字段中,也有可能因为一组语义上的属性使用的频度完全不同与另一组属性,即使他们语义上属于一个对象,也有可能将他们拆分成两个数据表来存储。

  • 从OO的角度,对象只关心自己的固有属性,不需要被唯一标识;但是,从关系型数据库的角度,一般一个数据表中的每一行数据都需要要一个唯一标识,且很可能是语义上没有意义的,如自增长的标识。

  • 从OO角度的优化,一般遵循SOLID这样的原则,以更正确的语义来组织对象;从关系型数据库角度的优化,往往为了查询性能,来修改字段的类型、长度,修改索引,甚至分表、分库。

  • ...

一个ORM工具想要通过简单的配置,完美的解决“O/R阻抗失衡”问题,在我看来几乎是不可能的。但是,一定程度上,通过灵活的配置支持绝大多数常见的映射策略,再加上提供可供用户通过自定代码来扩展的接口的话,应该还是可以相当接近完美的。

文化阻抗失衡

第二个难题,叫“文化阻抗失衡(Cultural Impedance
Mismatch)”,指的是关系型数据专家和面向对象专家之间的文化差异。关于这个难题的最经典的争论是“到底应该以关系型数据库的数据结构来驱动,还是以OO的对象模型来驱动程序的开发?”。

关于这个争论,OO专家的主要观点是:
我的业务才是程序的核心,数据库只是为我持久化数据的需求服务的,所以设计OO对象模型的时候,我不应该考虑如何存储到关系型数据库的问题;

而数据库专家的主要观点是:
数据才是公司的核心财富,数据的稳定性远远强于OO对象模型的稳定性,由OO对像模型来驱动数据库架构的设计,根本保证不了性能,数据库维护的成本根本不可接受。

其实,争论的真正原因即在于“关系型数据专家和面向对象专家之间的文化差异”。大大牛Scott W. Ambler的文章Why
Data Models Shouldn‘t Drive Object Models(And Vice Versa)
,很好的解答了这个问题。

归根结底,既然OO和关系型数据库都是不可缺少的部分,需要协同工作,希望做到完美的ORM,不仅仅需要好的ORM工具,更需要无论是OO专家还是数据库专家互相了解对方的技术和设计理念。对一个OO专家,一个ORM工具,可以对其所支持的常见的映射,提供直接的支持,但是,应用这些ORM工具的OO专家,必须了解关系型数据库,知道如何在不影响OO对象的语义的前提下,如何设计一个更容易映射到关系型数据库的对象模型,知道如何选择ORM工具所支持的映射方式,以及如何用自定代码扩展ORM所不能方便支持的映射方式;同样的,对一个关系型数据库的专家,也需要了解OO的语义,和可能发生的重构,从而,使得所设计的数据库结构,更容易映射到OO对象,更容易响应OO对象模型的重构。如此才是真正的和谐,真正的完美。

小结

综上所述,无论对一个OO专家还是对一个关系型数据库专家,都需要了解OO,并了解关系型数据库,了解ORM的基本原理。离开全面的知识,一个再完美的ORM工具也无法被正确使用,拥有这些知识,则能够充分利用已有的ORM工具来加速自己的工作,并且合理的或扩展现有的ORM工具,或用自定代码,实现ORM工具能力之外的ORM映射。这才是理想的ORM实践。下一章节,我们就来谈谈一下,ORM的基本原理。

如何进行O/R Mapping?


简单映射

1. Class <-> Table

一个Class一般可以映射为一个Table,一个Class的实例对应Table的一行数据。但是,一个Table中的每行数据,一般都需要有一个主键来唯一标识这行数据,而一个Class的每个实例,则不一定需要一个唯一标识。

2. Property <-> Field

一个Class的Property一般可以直接映射为Table的一个Field。但是,他们的数据类型不一定直接匹配。如果他们代表的数据类型的语义上可转换,则Field的类型,应大于等于Property的数据类型。如果他们代表的类型语义上不可转换,则需要在应用程序层面,进行自定义的转换。

继承映射

1. 单表映射整个继承体系

用一张数据库表存储整个继承体系中的所有Class的数据,数据表需要额外的标志字段来区分一行记录应该映射到继承体系中的哪一个Class,适合继承体系层次较少,总记录数相对较少,子类对父类的属性扩展也相对不那么频繁的情形。

单表映射整个继承体系的优点是读/写继承体系中的每个Class的数据,都只需操作一张表,性能较好,并且,新增继承类,或扩展Class属性都只需要增减一张表的字段就可以了,易于维护;主要缺点是,因为继承体系中所有的Class共享一张表,表中会有比较多的NULL字段值的数据,浪费了一些存储空间,同时,如果记录数过多,表就会更庞大,也会影响表的读写性能。

2. 一个Class映射一个具体表

所谓一个Class映射一个具体表就是每个Class对应一张数据表,并且,每个数据表冗余包含其父类的所有属性字段,并且,子类和父类共享相同的主键值。一个Class一个具体表方案适合需要较高查询性能,继承体系层次不太复杂,并且基类包含较少的属性而子类扩展较多属性,并且能够承受一定的数据库冗余的情况。

一个Class映射一个具体表方案的优点主要就是查询性能好,读操作只需操作一张表,和实体数据的对应结构清晰,数据库表迁移和维护会比较方便;主要的缺点是数据冗余较大,因为每次插入一条子类数据时,同时要插入一份子类包含的父类字段的数据到所有父类层次表中。

3. 一个Class映射一个扩展表

所谓一个Class映射一个扩展表是指继承体系中的每个Class对应一张数据表,但是,每个子类不冗余包含父类的所有属性,而只是包含扩展的属性和共享的主键值。一个Class映射一个扩展表方案适合继承体系非常复杂,结构易变,并希望最大程度减少数据冗余的情形。

一个Class映射一个扩展表方案的优点是结构灵活,新增子类或插入中间的继承类都很方便,冗余数据最少;但是缺点是,无论读还是写操作都会涉及到子类和所有的父类。读操作时,必须自然链接查询所有的父类对应的数据表,而插入或更新数据时,也需要写所有的父类表。

4. 通用的表结构映射所有的Class

这种方案其实不仅支持用一张表存储一个继承体系,它甚至可以支持,用一张表存储任意数量的不同Class。它的原理是元数据驱动。这张表的每一行,包含一个类型的标识字段,一个表示Class的属性名称的字段和一个表示Class的属性值的字段。在运行时,通过唯一标识取出描述一个Class实例的所有Property的值,再根据Property的名称来映射。

关于各种继承映射的实例分析和详细的优劣比较,请参见:Scott W. Ambler的O/R
Mapping In Detail

关联映射

1. 一对一关联、一对多关联(包含一对一和一对多的自关联)

所谓一对一关联,实际上还可以分为三种情形,即0..1 - 1,1 – 1,1 – 0..1三种情形;而一对多关联则分为* - 1和1 - *。

以下三种方案中第1)种为最常用的映射方案,后面几种是在某些特殊情形下可参考的方案:

1)
最常用的方案为为需要其他对象引用的类对应的表增加一个到被引用对象对应表的外键即可,只不过,与表对应的实体类代码中,对于一对多情形下的“多”这一端,需定义成集合类型;
2) 在Hibernate称为“组件(Component)映射”,举例来说,假如Person类包含一个Address成员类型的属性,而Address由City,Street,ZipCode三个成员属性组成,假如Address除了与Person关联不被其他对象使用,则我们可以考虑只用一张数据表Person来持久化Person和Address这两张表,Person数据表包含Person类中除Address的属性和Address类中的所有属性的集合,当然,这时需要在元数据中特别指明映射关系;
3) 还有一种针对上面的方案中的Person,Address两个类的持久化方案则是将Address类型的所有属性先序列化,再存入Person表的字段Address中,这样也可以只用一张表来持久化两个类,当然,本方案中这种被序列化对象成员数据量应尽量小;
4) 还有一种方案是共享同一主键值的一对一关联。即将原本可以同属于一个表中相对使用不太频繁的字段提出来放在另一张表中,这样,这两张表的记录就可以通过一个相同的主键进行关联。

2. 多对多关联(包含多对多的自关联)

所谓多对多关联自然就是* - *这种情形了。一般都需要一张包含关联双方主键的关联表,在取数据时,需要链接该关联表和数据表。

英文资料


时间: 2024-07-29 12:40:54

理解O/R Mapping的相关文章

Hibernate O/R Mapping模拟

作为SSH中的重要一环,有必要理解一下Hibernate对 O/R Mapping的实现. 主要利用java的反射机制来得到完整的SQL语句. 准备工作: 1. Object Student实体类: public class Student { private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = i

什么是O/RMapping?为什么要用O/R Mapping?

什么是O/R Mapping ? O/R Mapping 就是有一大堆的类库,我们调用它的时候用面向对象的方式来调,它帮我们翻译成为面向关系的方式. 为什么要用O/R Mapping? 我们编程会更加的面向对象,更加的简单,不用再去考虑SQL语句的问题了. 1,JDBC操作数据库很繁琐 2,Sql语句编写并不是面向对象的 3,可以在对象和关系之间建立关联来简化编程 4,O/R Mapping简化编程 5,O/R Mapping跨越数据库平台 比如hibernate,可以把方言改成从mysql改成

O/R Mapping框架:Ibatis与Hibernate之间的区别

hibernate 是当前最流行的o/r mapping框架,它出身于sf.net,现在已经成为jboss的一部分了.ibatis 是另外一种优秀的o/r mapping框架,目前属于apache的一个子项目了. 相对hibernate“o/r”而言,ibatis是一种“sql mapping”的orm实现. hibernate对数据库结构提供了较为完整的封装,hibernate的o/r mapping实现了pojo 和数据库表之间的映射,以及sql 的自动生成和执行.程序员往往只需定义好了po

NHibernate官方文档中文版--基础ORM(Basic O/R Mapping)

映射声明 对象/关系映射在XML文件中配置.mapping文件这样设计是为了使它可读性强并且可修改.mapping语言是以对象为中心,意味着mapping是围绕着持久化类声明来建立的,而不是围绕数据表. 要注意的是,尽管很多NHibernate使用者选择手动定义XML文件,但是仍然有很多工具可以用来生成mapping文件,包括NHibernate.Mapping.Attributes 库和各种各样基于模板的代码生成工具(CodeSmith, MyGeneration). 让我们用一个mappin

[NHibernate]O/R Mapping基础

系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) 引言 对象和关系数据库之间的映射是用一个XML文档(XML document)来定义的.这个映射文档被设计为易读的,并且恶意手工修改.映射语言以.NET为中心的,意味着映射是持久化类的定义来创建的,而非表的定义. 请注意,虽然很多Hibernate用户选择手工定义XML映射文档,也有一些工具来生成映射文档,包括XDocl

Object Relation Mapping ORM

对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.从效果上说,它其实是创建了一个可在编程语言里使用的--"虚拟对象数据库". 面向对象是从软件工程基本原则(如耦合.聚合.封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别.为了解决这个不匹配的现象,对象关系映射技术应运而生. 对象关系映射(Object-Rela

关于VO、PO的理解——java的(PO,VO,TO,BO,DAO,POJO)解释

O/R Mapping 是 Object Relational Mapping(对象关系映射)的缩写.通俗点讲,就是将对象与关系数据库绑定,用对象来表示关系数据. 在O/R Mapping的世界里,有两个基本的也是重要的东东需要了解,即VO,PO. VO,值对象(Value Object),PO,持久对象(Persisent Object),它们是由一组属性和属性的get和set方法组成.从结构上看,它们并没有什么不同的地方.但从其意义和本质上来看是完全不同的. 1.VO是用new关键字创建,由

[转]JAVA三大框架SSH和MVC理解

Java—SSH(MVC) JAVA三大框架的各自作用 hibernate是底层基于jdbc的orm(对象关系映射)持久化框架,即:表与类的映射,字段与属性的映射,记录与对象的映射 数据库模型 也就是Model; struts提供的丰富标签是用于显示层View;同时struts也充当了实现Control的功能(不管是Struts1,还是Struts2),接收参数,视图分发. Spring是用于降低层与层间耦合度的,就是所有的类都可以由spring统一创建,用时只需注入即可(IOC) 从功能上来说

ORM是什么?如何理解ORM

一.ORM简介         对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术.简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中.那么,到底如何实现持久化呢?一种简单的方案是采用硬编码方式,为每一种可能的数据库访问操作提供单独的方法.         这种方案存在以下不足:         1.持久化层缺乏弹性.一旦出现业务需求的变更,就必须修改持久化