从类模型转换到数据库表结构的思考
从类模型转换到数据库表结构的思考
Aaron
从类模型转换到数据库表结构,或者说从UML类图转换为ER图,这个功能并不实用。
为什麽呢?
如果你的类模型能够完整地转换为表结构(每一个表都能找到与之相关的类,如果类之间是多对多关系,那么会增加一个中间表),那么说明你的类图是不完整的,它们仅仅抽象了领域模型的数据资源,并没有抽象出领域模型的业务行为,因为表结构只能描述静态资源,不能描述业务行为。也就是说,你的类模型是“失血模型”。
我们这里不讨论“失血模型”与“充血模型”的优缺点,有兴趣的朋友可自己去javaeye上搜索。
当然,你也可以认为,即使使用了充血模型,仍然可以从类模型映射为表结构。但从实际情况来看,数据表的设计与类模型的设计常常有着差异。毕竟从DBA的角度抽象对象(其实DBA只能抽象对象的数据),与程序员从OO的角度抽象对象(数据和业务行为)是不同的。
数据表的关系是二维的(关系性数据库),而类模型的关系是三维的(OO的特性),类模型的关系远比数据表的关系复杂,比如类之间有继承,而表结构是不可能继承的。这就是数据库与类模型的“阻抗”。
正因为数据库与类模型有阻抗,所以从类模型生成表结构是不现实的,它们不可能有标准的规则进行完美转换。除非是小项目,否则从类模型生成表结构,或者从表结构生成类模型,一定需要人工调整——这是致命的问题,哪怕需要手工调整的差异再小,日后的维护也足以让人心惊胆颤——项目的bug常常出现在被忽略的细节处。
顺便说一句,小项目(小项目不是指代码少或者数据表少,而是指业务逻辑简单)的开发根本不用这么麻烦,不需要领域模型设计的垂直开发模式永远最快。而不用领域设计,已经超出本文的讨论范畴,本文只讨论横向切片----多层开发模式。不明白的朋友可以看看双鱼座的《只有企业级项目才特别需要使用ORM》一文。
那么,在设计时该以类模型为核心,还是以数据表为核心?
换个方式说,是先设计出类模型,然后参考类模型设计出数据表;还是先设计出数据表结构,然后再参考数据表结构设计出类模型?
无论先设计哪一样都不重要,甚至两者并行独立设计也可以。关键是两者不能依赖(参照)对方。
类模型有类模型的设计模式,数据表有数据表的设计原则。面向对象与数据库三大范式既有共同点,也有分岐,“低耦合,高内聚”,这样的面向对象的设计思想在设计数据表时完全用不上。
说了这么多,我的意思就是,类设计和数据表的设计同等重要,不可偏废。只是两者考虑的角度不同,类设计时主要考虑可维护性,数据表设计时主要考虑效率。
如果从类模型完全映射为表结构,那么这样的数据库性能必定很差;反之从数据表结构完全映射为类模型,那么这样的系统可维护性必定很差。
这两种设计方式,一定会让DBA和程序员其中一方困惑,觉得难以理解,难以使用。
那么如果我们在设计系统时,把类模型和数据表结构分开独立设计,互不干扰是否可行?
答案是肯定的,而且本来就应该如此(分开设计)。
类模型和数据表结构的设计,应该参考业务模型,根据各自的特点(OO和范式)进行设计。
那么类模型和数据表结构的阻抗该如何消除?
ORM可以消除类模型和数据表之间的阻抗。
ORM是目前最通用的解决方案,并且ORM已经被证明是行之为效的方案。ORM是类模型和数据表之间的桥梁,有了它,类模型和数据表才能平滑转换(映射)。
现在已经有了许多成熟的ORM方案,其中不乏庞大的工程(好吧,我承认我水平有限,Hibernate在我眼中就是大工程了,但在真正的高人面前,Hibernate只是“轻量级解决方案”)。
但我们不要被这些ORM方案的“花哨”功能迷惑:除了类模型和数据表之间的映射,其它诸如缓存、类SQL查询等等,都不是ORM的核心。ORM的核心就是“映射”而已,去掉缓存和类SQL查询之后,Hibernate仍然可以称为ORM,但去掉映射功能之后,Hibernate决不是ORM。
ORM的映射也有层次,比如iBatis和Hibernate的映射层次就不相同。ibatis是基于表映射,而Hibernate才是真正的类模型映射。两者最明显的差异就是,Hibernate支持巢状对象(多对多,对象中嵌入子对象),而iBatis不支持。所以也有人认为iBatis不是“纯粹的”ORM。
ORM的一些“缺陷”可以看看双鱼座的《ORM之硬伤》一文。
以下是我对ORM的一些理解:
1. 使用ORM不是为了赶潮流。小项目我喜欢垂直开发,一个ADO.NET强类型数据模型从Business Layer贯穿UI Layer到底,简单直接。
2. 使用ORM不是为了所谓的提高效率。ORM能提高效率是个笑话:论持久化,ORM的效率比不上ADO.NET;论运行效率,你认为ORM的反射和XML能快到哪里去?好吧,有人提出了ORM打开缓存之后,效率比多次直接查询数据库还快,但缓存关ORM什麽事?没有ORM难道我就不缓存了吗?
3. ORM是为类模型和数据表服务的,不要让ORM“喧宾夺主”,不要无限夸大ORM的作用。
4. 有些ORM侵入性太强,和类模式混合在一起。比如使用Castle的ActiveRecord时,类就必须从ActiveRecordBase继承。这样导致领域模型被“污染”了,增加领域模型的理解和维护的难度。而且C#是不支持多继承的,从ActiveRecordBase继承就限制了程序员的自由发挥。
5. ORM用来消除类模型与数据表之间的阻抗,其目的是为了用OO的思想来实现领域模型。
当我们使用面向对象的数据库代替关系性数据库之后(比如DB4O),ORM就没有存在的必要了。