ORM(Object Rela
tional Mapping)技术为什么是有效的?对这个问题一般的答案是ORM解决了面向对象技术和关系数据库之间的阻抗匹配问题。但是任何一种成功的技术,它的支持理由都不会是单一的。在Witrix平台的实践中,ORM的如下几个特性是关键性的:
1. 主键和对象之间的一一对应关系。在Web应用中,前台浏览器持有的只能是对象的某种表示, 因此一种locator机制是最基础的要求。
2. Container结构。Container所拥有的信息包括其所有元素以及元素之间的关系。因为Container具有全局知识,所以它可以解决对象图中的循环依赖问题。正是因为session中一级缓存的存在,才能在实体延迟加载的情况下保证对象引用的唯一性,保证a.b.c.a == a。在程序设计中,所有支持对象图的Container结构都是non-trivial的,都必然是有价值的。例如spring容器在配置管理方面最重要的价值就在于可以管理循环依赖的对象创建过程。而在witrix平台的前台所定义的ControlManager管理器其核心作用就在于协调前台控件之间的消息响应依赖关系。从spring的配置文件可以清楚的看出,bean的声明过程是顺序进行的,但是bean的创建过程是非顺序结构的。在数学上,我们说系统的拓扑(topology)发生了变化。
A a = new A(); B b = new B(); a.setB(b); b.setA(a);
3. 对象作为复杂属性的集合。关系数据库中保存的都是原子性数据,每一列都是不可再分解的原始数据类型,数学上称之为标量(scalar). 而ORM引擎返回的直接就是嵌套的复杂数据类型,因此不再需要一个额外的造型过程.虽然总是返回全部数据列不是很优化,但是这种强制性的较粗的处理粒度使得前台程序可以有更多的选择自由.
4. 对两两关系的内蕴表达及充分利用.关系数据库中所保存的是系统分解后的表示,即关系被分解了而不是被表达了,外键对数据关系只起到一种约束作用,它对于查询语句的构建并没有直接的影响.所有数据之间的关系都必须在查询的时候明确指定出来,即调用者必须拥有数据关系的知识,而不是数据本身拥有这些知识.在如下的sql语句中
select * from a, b
where a.fldA = b.fldB
and a.fldC = 1 and b.fldD = 2
a.fldA = b.fldB 可以称为关联条件,而a.fldC=1可以称作是坐标条件.SQL的复杂性很大程度上来源于我们频繁的需要在各处指定完全一样的关联条件而无法把它们抽象成可复用的组分.在ORM所提供的对象空间中,对象之间的两两关联只要指定一次,就可以在增删改查等各种操作过程中起到作用,特别是在对象查询语句中,可以通过两两关联自动推导出多实体之间的关联关系,虽然推导出的结果未必是最优化的.
select from a where a.fldC = 1 and a.b.fldD = 2
在Witrix平台中,我们做了大量的工作以确保对象上的复合属性(例如a.b.fldD)和本征属性(例如a.fldC)在使用上是完全等价的.这些工作的结果并不仅仅是减少了一些应用层的代码量,它使得系统结构发生了深刻的变化.复合属性把单表模型推进到了业务主题模型,使得单一业务对象可以聚集某一范围内的相关结构信息,这才使得witrix的模型分解技术成为可能。因此在我们看来,HQL是hibernate价值的集中体现.
5. POJO提供了纯粹的first class的持久化结构空间.采用POJO结构可以充分利用现有语言及开发工具中的一系列基础设施,大大降低了持久化结构的构造成本.而透明的get/set操作使得我们可以完全基于相对知识对持久化结构进行变换,在完全不依赖外部环境信息(例如数据库连接和ResultSet界面)的情况下解决系统的主要业务结构问题.这一切成为可能在技术上都源于AOP(Aspect Oriented Programming)所引入的重新诠释的能力,它使得我们可以将对象上的某种相对操作重新诠释为对数据库的相应操作.
6. Entity具有活动能力.Entity并不是完全被动的数据容器,而是可以定义复杂动作的对象.在Witrix平台中,后台程序大致具有如下结构
Entity ---- 结构问题
Handler ---- 业务动作定义
BizFlow ---- 动力学问题
Handler类似于J2EE中传统的Service层,只是一般实现的方法要少的多.而BizFlow是某种结合了界面表示的流程引擎.基于实体结构使得系统在细粒度处具有某种活动能力,便于我们构造一些局部结构来解决问题,因而也就缓解了大量操作在Service层的堆积过程,有利于维护系统整体结构的对称性.
----------- ==> -------------|--
----------- |--
通过对于ORM技术的理论分析,Witrix平台采取了一些和一般J2EE架构不同的设计.实际上目前J2EE架构下的常见的DAO模式在使用ORM技术的情况下往往不是合适的选择,因为DAO的一般设计是封装某个实体相关的操作,它直接破坏了ORM的container结构。原先我们只需要EntityManager.get(entityClass, id)这一通用方法既可得到各种数据实体对象,而现在需要对每种实体调用不同的Dao函数,显然这是对系统结构的重大破坏。在Witrix平台的设计中没有独立的DAO层,它通过通用的EntityDao统一完成所有对象的存取过程,而不是每个XXXManager继承公共的Dao类。即整个系统架构中尽量维护数据存取过程的统一性而不是实现它的分散化。
在Witrix平台的Workflow引擎,Wiki引擎等模块的设计中,IWorkflowStore和IWikiStore等类的设计类似于DAO模式,是对存取方式的一种封装。在比较复杂的模块中,对于存取逻辑做出一定的封装是需要的。但是注意到Store类的设计和实体框架的设计相比,其结构可分解性要相差很多,它基本上只提供对外的服务接口。如果我们能够对于文件系统等存储设施作出充分多的工作,我们一样可以对于非关系数据库的某些存取形式完成Container结构,只是这个工作量过大,而我们一般并不需要对非通用的存取结构掌握如此充分的知识。
实体结构隐含的扩展了系统的同时性视图,a.b.c.a == a 所隐含表达的事实是a,b,c是同时存在的.在某些时候,例如当我们需要将系统结构顺序化,序列化的时候,这种同时性会成为一种障碍.因此Witrix平台中数据同步所使用的ETL(Extract Transform Load)引擎是基于表结构(分解后信息)的,而不是基于实体结构(关联信息)的.实际上,关系模型在某种意义上是系统分解后的必然结果,因此随着我们对系统的理解的粒度要求越来越精细,很可能最终需要我们明确意识到关系对象本身的存在性,最终实体模型会非常近似于关系模型.只不过在实体模型级列中我们选择的余地更大,关系模型可以看作是它的某种极限.理想的情况是在不同的时刻我们可以使用不同的关系抽象,只是受限于目前的实现技术,在系统构建时刻基础的关系结构往往就被固化下来.