1.2 ORM模型介绍

当Java作为一门语言出现时,一个比较重要的工作任务就是要操作数据库。这就不可避免地要涉及数据库的接口。Sun公司从1996年就把JDBC加入了Java平台的1.1版本之中,将其作为RDBMS资源管理和数据访问的标准低级抽象层。但是,直接使用JDBC API是相当麻烦的,并且开发的效率也比较低。这样,Java ORM模型就横空出世了。

1.2.1 什么是ORM

O/R Mapping全称Object Relation Mapping,即对象关系映射,把对数据表映射为对象类,将在数据库中直接进行的原始操作演变为对类的属性和方法的操作,而间接更改数据表的数据。

通常,实现ORM框架一般包括以下四部分:

① 对映射类进行CRUD(新增、查询、修改和删除)操作的API;

② 规定Object与Relational之间的映射规则,一般采用metadata进行表示;

③ 规定类和类属性相关的查询规则;

④ 实现ORM中对数据库操作的事务管理。

实现ORM框架也有很多其他的扩展功能,包括缓存处理、分布式事务管理、多种数据库之间的无缝迁徙等等。

1.2.2 ORM的实现方式

关系数据模型则基于数学原理,特别是集合论的原理,这些原理都能在数学理论上得到完全的证明。而对象模型基于程序设计的一些原则,类同于一种解决问题的工具,并没有得到公理或数学理论的支持。面向对象设计的目标是通过把一个业务过程分解成具有状态和行为的对象来进行建模的业务过程,而关系数据库的设计目标是规范化数据以消除数据冗余度。两种不同的理论基础导致对象模型与关系数据模型之间的阻抗不匹配。面向对象模型和关系数据模型的阻抗不匹配使得进行对象关系映射时存在着许多问题。关系数据库不支持诸如类、继承、封装和多态等面向对象的概念。对象关系映射是在对象与数据库之间进行映射的一种技术,对象间的关系、类及其属性必须以某种方式映射为关系数据库中的数据库关系、表和字段。

针对对象模型与关系数据模型间的阻抗不匹配,人们提出了一些解决方案来实现对象模型向关系数据库模型的映射,由于数据库是以二维表为基本管理单元的,所以在使用关系数据库的面向对象软件应用中对象模型最终是由二维表以及表间关系来描述的,而这其实就是一个类与数据库表的变换过程。从某些方面看来,在对象与关系数据库表的行之间有着很多共同点。对象是类的实例,它的内部数据的结构以及其他一些特征由某个类定义。类似地,关系数据库的脚本定义了数据库表的结构,例如表包含哪些字段等。类的实例与数据的行以相似的方式存储数据。通常一个类可以映射为一个或一个以上的关系数据库的表,类属性将映射成关系数据库中的字段,而对象间的关系可以通过使用关系数据库中的外键来维护。对象关系映射的解决方案一般是把每个对象映射到数据库表的单个行上,这一行通常来自一个表,也可以由一个表的连接操作产生。

1.ORM中对象与数据库表之间的映射机制

ORM的实现方式也就是O/R Mapping的映射机制,一般有以下几种情况。

(1)类属性和数据库的数据表与列建立一种随机的映射关系

也就是说,对象类和数据库表并非一一对应,同时对象类中的属性也不是与数据库表中的列并非一一对应。一个类属性可对应1或多个实体表的字段。同样,一个实体表可以对应1个或多个实体类的属性。这种实现模式主要还是根据业务逻辑来划分对象的,一方面一个业务逻辑类可以获取一个数据库表的一部分字段,同时还要获取另一个或几个数据库表的部分或全部字段。另一方面一个数据库表可以映射成为多个对象,分别应用在多个业务实体类中。这种方式的好处有两点,一是映射的对象结构简单、易于使用,二是避免每次构造对象的时候传送大量的不相关的数据。

当然,基于这种模式,存在实现对象在数据库中的唯一标识的问题。其实这有两种解决方案,第一种解决方案还是采用数据库主键来实现唯一标识。如果一个实体类映射的是一个数据库表,可以采用数据库表的主键来形成实体类的唯一标识。如果一个实体类映射的是多个数据库表,可以把多个数据库表的主键组合形成一个实体类的唯一标识。第二种解决方案是采用无业务意义的字段OID作为各个实体对象的主键。这样OID也作为类与数据库映射时的对象的唯一标识。

(2)实体类和数据库表一一映射

这是一种最简单的实现模式,即数据库表与对象一对一,即一个数据库表映射为一个Java对象。所有的表字段(field)映射为Java对象的属性(attribute)。但是不同层次的实体类映射到数据表时,应根据数据库表的关系来进行。这样也有三种模式。

① 一个类层次对应一个数据表

这里所说的一个类层次,指的是父类及其所有子类。将父类和子类中有持久性需求的属性设置为同一数据表的字段。此方法实现起来简单,并且较好地支持多态。因为不同的子类可以用一个标志位加以区分。子类实例的转换比较容易实现,但是,类与数据库之间的耦合程度高。由于子类所特有的属性被所有类层次中的对象所共有,数据库中冗余字段较多。

② 一个实体类对应一个数据表

各个子类所特有的属性,联合从父类中继承的公共属性,构成表的结构。父类不映射为数据库中的实体表,它只作为子类公共属性的载体。这种映射模型使得类属性值的保存和对象还原实现方便。缺点同样是类结构与数据库的耦合程度高,特别在父类属性变列时,所有从此继承下来的子类都需要进行变更。此外,对多态性的支持也较差。子类的角色转换需要在子类对应的数据表之间准确地传递适当的属性值,同时,需要赋予新的OID。这种情况下,就不如第一种映射模型,用同一个OID,在同一张表内就可以实现不同子类之间的转换。

③ 一个类对应一个数据表

无论是父类还是子类,只要类中的属性有持久性保存的需要,就将类映射到数据表。子类的表以父类类表的OID为外键。在关系数据库中最大程度地实现了类的多态性。与前两者比较,对象与数据库的耦合程度是最低的,某一个类属性的变更引起的表结构变动最少。这种方法的缺点在于生成子类实例时,继承层次多的子类的属性值还原很困难。由于子类的公共属性包含在父类对应的数据表中,当需要单独获取子类实例时,需要从多个数据表中获取数据合成完整的实例。当类的层次较多时,子类的访问可能会成为系统与数据库交互的瓶颈。

(3)实体类和数据库视图映射

第3种映射方式是根据数据库视图来构造对象,数据库视图是一种虚拟表,它可以合并多个数据库表,然后再把这个数据库视图映射成一个实体对象。最为常见的是在数据库中体现为主从表的结构,映射成一个实体对象。

2.类间关系映射为键值

数据库表的约束决定了实体类关系中的关联和聚合。

一对一、一对多的关联是通过在关联的某一方(一般在关联角色多重性≥l的一方)引用对方的OID,并根据关系的紧密程度为外键的字段加上非空以及唯一性的约束。在生成相关联的类实例时,可根据外键自动获取另一方的实例。多对多的关联需要创建关联表。关联表是统一的以OID作为主键,同时以关联角色双方的OID作为联合外键。

聚合关系映射到数据库中与通常所说的主附表结构类似,在聚合关系中的子类对应的数据表中含有指明父类的OID的域。特别是强制型聚合(或称之为组合),子类单独存在是没有意义的,必须与父类同时存在、同时消亡。当然,数据库键值本身只是这一关系的体现。

数据库表之间主键外键关联映射的关系对象模型中存在着三种关系,分别是一对一、一对多、多对多。体现在数据库中分别是一个主键对应一个外键,一个主键对应多个外键,中间表(join table)。

① 一对一:这种情况可以直接映射成在对象之间保持一个引用关系,一个对象持有对另一个对象的引用。具体体现在对象中实现一个方法,该方法的返回值即它的一对一关联对象。如果这种关系是双向的,那么必须在两个对象中都实现这样一个方法,如果关系是单向的,那么由方向性决定在哪个对象中实现。

② 一对多:这种关系有两种类型,分别是关联(association)和聚合(aggregation)。对于关联,仍然体现为引用关系。不同于一对一的是,一方对象持有的是一个集合的引用,该集合中的元素是多方对象,多方对象持有的是一方对象的引用。一方对象实现的方法的返回值是一个集合,集合中的元素是多方对象;多方对象实现的方法的返回值就是一方对象。当然可根据业务需求决定关系的方向性,从而决定在哪个对象中实现对应的方法。对于聚合,需要在一方对象中增加一个集合的属性,该集合中的元素为多方对象。同时一方对象的增、改、删、查也要加入相应的操作以实现多方对象的对应操作。而多方对象的实现可以根据实际需求来决定是否需要实现独立的增、改、删、查方法。

③ 多对多:这种关系可以看作是一个双向的一对多,都把自己看作是一方,对方看作是多方。两个对象分别持有一个集合的引用,集合中的元素即为对方对象。如果将数据库的中间表映射成一个对象,那么可以将多对多关系的两个对象分别实现为对应中间表对象的一对多,即这两个对象都看作是一方,而中间表对象则看作是多方。从而利用一对多的实现方式去实现这种关系。

3.面向对象操作映射为数据库操作

ORM框架的一项重要工作就是将对数据库的操作封装成实体类的方法。其好处就在于封装了操作的细节,开发人员根本不用关心如何去连接数据库,如何发送SQL语句,如何取各个字段,他只需调用一个CRUD(Create、Read、Update和Delete)方法,该方法完成一系列的底层操作,返回的是一个构造好的对象,然后他就可调用相应的get方法取得他所需要的字段的数值。

ORM框架的方法也就是增、改、删、查等基本操作,而实体对象属性也是由数据库表中的字段所构成的。ORM框架在构造过程中,实际上把CRUD方法,根据对象属性与数据库表的映射字段转化为数据库操作中的insert、select、update和delete等SQL语句。

4.事务管理

由于ORM框架封装了数据库的存储操作,软件开发人员不能处理底层存储细节,没有利用数据库的事务管理机制的可能。可以通过两种办法解决这个问题,一是采用独立的事务处理机制,不采用数据库本身的事务管理。二是在映射对象中重载多个保存方法,仍然采用数据库的事务管理机制,使得某一个重载的方法可以提供给软件开发人员事务处理的能力。在Java语言中,可以由软件开发人员提供一个java.sql.Connection接口,将这个接口作为一个参数传给保存的方法,从而可以使得软件开发人员有手工控制事务的可能。

5.ORM的性能

ORM框架需要频繁地跟数据库交互,以下几个方面对性能有影响。

① 数据库的连接:数据库的连接对象是非常昂贵的资源,不同的获取数据库连接的方式将对性能产生极大的影响。这可以采用连接池的办法来解决,每次从连接池中获取数据库连接,将极大地提高性能。

② 大量数据的传输:由于应用程序使用的是对象,而对已有数据库记录的对象的生成需要构造它的所有属性,也就是要将该条记录所有字段的数值赋给这个对象。而业务逻辑有可能仅仅关心其中几个字段的数值,其他字段数值的传输可以说是浪费的,因而可以采用分段获取的模式。

③ 对象的频繁获取:每次应用程序使用已有的持久化对象都要查询一次数据库,然后构造这个对象,对于需要频繁使用的对象来说,每次都需要查询数据库。对于这种情况,可以采用缓冲的机制,将一些频繁使用的对象缓冲起来,再次使用的时候先从缓冲池里面查找,如果没有找到,然后再查询数据库。

1.2.3 常用的ORM框架

常见的ORM框架包括Hibernate、iBATIS、TopLink、Castor JDO、Apache OJB等,分别介绍如下。

1.Hibernate

Hibernate是一个开放源代码的O/R Mapping(对象关系映射框架),它对JDBC进行了轻量级的对象封装,使Java程序员可以随心所欲地使用对象编程思维来操纵数据库。其官方网址:http://www.hibernate.org

2.iBATIS

iBATIS也是开放源代码的O/R Mapping,但这是一种“半自动化”的ORM实现。所谓“半自动”,iBATIS以SQL开发的工作量和数据库移植性方面的让步,为系统设计提供了更大的自由空间。其官方网址:http://ibatis.apache.org/

3.TopLink

TopLink是Java对象关系可持续性体系结构,原属于WebGain公司的产品,现在被Oracle收购,并重新包装为Oracle AS TopLink。TopLink为在关系数据库表中存储Java对象和企业Java组件(EJB)提供了高度灵活和高效的机制。TopLink提供了一个持久性基础架构,使开发人员能够将来自多种体系结构的数据(包括EJB、CMP和BMP)、POJO、servlet、JSP、会话Bean和消息驱动(Bean)集成在一起。

4.Entity Bean

Entity Bean它提供了一个持久性数据的面向对象的表示。不同于对象关系映射,Entity Bean对于关系数据库没有限制;它描述的持久性信息可以来自一个企业信息系统(EIS)或者其他的存储设备。

5.Castor JDO

Castor JDO是ExoLab Group下面的一个开放源代码的项目,它最大的特色就是实现了大部分的ODMG OQL规范,其原理是通过Java反射API去实现属性的设置和读取。它的主要API和数据接口为:JDO-like、SQL、OQL、JDBC、LDAP、XML、DSML。它支持分布式目录事务处理和时间;提供处理XML、Directory、XADirectory的类库,提供从XML到Java类的转换机制。其官方网址:http://castor.exolab.org

6.Apache OJB

Apache OJB(Object Relational Bridge)是Apache下面的一个开放源代码的项目。Apache OJB是一种对象关系映射工具,能够完成从Java对象到关系数据库的透明存储。OJB使用基于XML的对象关系映射,映射发生在一个动态的元数据层,使得通过一个简单的元对象协议(MOP)在运行时就可以操作元数据层去改变存储内核。其官方网址:http://db.apache.org/ojb/

7.Torque

Apache Torque是一个使用关系数据库作为存储手段的Java应用程序持久化工具。Torque是Apache下面的一个开源项目,由Web应用程序框架Jakarta Apache Turbine发展而来,但现在已完全独立于Turbine。

1.2.4 ORM模型和持久层框架

现在很多书籍都把ORM模型和持久层作为一个概念来进行说明。笔者认为这两者还是有区别的。这里有几个基本概念要区分,包括持久化、持久层、ORM等。

持久化,英文即“Persistence”,就是把内存中的数据对象保存到可永久保存的存储设备中。持久主要应用是将内存中的数据存储在关系型的数据库中,当然也可以指存储在磁盘文件等。持久层就是专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。

O/R Mapping是把对数据表映射为对象类,或者把对象类映射为数据表。

在细微的比较上两者还是有一点差异的。

① ORM模型主要是实现对象和关系数据直接的映射。持久层是对象处理和关系数据库处理中的一个交互层,在这个交互层主要是实现内存数据与硬盘数据的一致性和统一性。

② ORM模型是一种实现手段,而持久层是一种实现技术。

③ 持久层框架的概念要比ORM模型框架要大,即持久层框架一般都是包括ORM框架,而ORM只是实现持久化技术中的一种。

从严格意义上来讲,iBATIS应该趋向于是ORM框架。这样才归属持久层框架。在这里简单地介绍一下几种持久层框架的实现方式。

① 主动域的对象模式

主动域的对象模式是在实现中封装了关系数据模型和数据访问细节的一种形式。在J2EE架中,EJB组件分为会话EJB和实体EJB。会话EJB通常实现业务逻辑,而实体EJB表示业务实体。实体EJB又分为两种:由EJB本身管理持久化,即BMP(Bean-Managed Persistence);有EJB容器管理持久化,即CMP(Container-Managed Persistence)。BMP就是主动域对象模式的一个例子,BMP表示由实体EJB自身管理数据访问细节。主动域对象本身位于业务逻辑层,因此采用主动域对象模式时,整个应用仍是三层应用结构,并没有从业务逻辑层分离出独立的持久化层。

② JDO模式

JDO(Java Data Objects)规范是Sun公司制定的描述对象持久化语义的标准。严格地说,JDO并不是对象-关系映射接口,因为它支持把对象持久化到一种存储系统中,包括关系数据库、面向对象的数据库、基于XML的数据库,以及其他专有存储系统。由于关系数据库是目前最流行的存储系统,故许多JDO的实现都包含了对象-关系映射服务。

③ CMP模式

在J2EE架构中,CMP(Container-Managed Persistence)表示由EJB容器来管理实体EJB的持久化,EJB容器封装了对象-关系的映射及数据访问细节。CMP和ORM的相似之处在于,两者都提供对象-关系映射服务,都把对象持久化的任务从业务逻辑中分离出来。区别在于CMP负责持久化实体EJB组件,而ORM负责持久化POJO,它是基于普通的JavaBean形式的实体域对象。