关于O/R Mapping的思考,我们应该选择什么?

简介:

最近对ORM方面的东西感兴趣,有一些想法,想和大家讨论一下。

我觉得Hibernate应算算是一个经典且功能强大的ORM框架。

它最大的有点是,你可以完全用面向对象分析与设计(OOAD)的思想来设计好所有的类,然后该框架就可以以非常人性化的方式为你获取对象或者持久化对象。

但同时我也发现它的一个缺点,就是当对象与对象之间的关系比较复杂的时候,ORM配置文件会很复杂,且比较难维护。并且当我们要对多个表进行关联查询时,虽然框架提供给了我们很多方便的接口让我们可以通过设置对象的属性的方式来告诉框架我们需要什么数据,然后框架会自动为我们生成一个复杂的SQL语句,最终返回给我们数据,但我发现当表关联较多并且数据量比较大的时候,往往性能不佳。

仔细想了之后,我觉得Hibernate的设计者的初衷是好的,就是想让我们尽可能的不用去关心数据库,让我们可以完全以面向对象的方式去实现我们的业务逻辑。但就我个人观点,我觉得它为我们做了太多的事情,比如一些原本属于业务逻辑的工作也由它帮我们自动完成了。比如订单(Order)和订单明细(OrderItem),一个Order包含了多个OrderItem,当我们要删除一个Order时,Hibernate能帮我们自动删除该订单下的所有OrderItem(当然你也可以选择不级联删除)。再比如,当我现在要显示一个帖子的详细页面,该页面需要显示帖子(Thread)内容以及所有的回复(Posts),如果用Hibernate,我们只需要查询一个Thread即可,而它下面的所有的Posts会自动被级联查询出来(因为我们已经在配置文件中配置好了,选择了非LazyLoad的情况时)。当然,你或许会觉得这样不是很好吗?全部自动化,你就很省心了,不是吗?确实,当业务逻辑比较简单时,这样没什么问题,但如果当业务逻辑比较复杂时,我们还能通过简单的不用动脑子的方式来配置映射关系,并且用简单的方式把数据查询出来吗?比如下面这个例子:

我现在要查询出一个论坛中某个版块下具有某种角色的所有用户,如果我们把这个角色理解为“版主”,那么就是相当于要查询出论坛中某个版块的所有的版主。
我可能会建如下一个表:

SQL code
CREATE TABLE [ tb_SectionRoleUsers ][ SectionId ] [ int ] NOT NULL[ RoleId ] [ int ] NOT NULL[ UserId ] [ int ] NOT NULL )


SectionId表示版块ID,RoleId表示角色ID,UserId表示用户ID。对于这个表而言,必须是以两个字段组合去查第三个字段才有意义。比如查某个版块下具有某个角色的所有的用户,或者查某个版块下某个用户具有哪些角色。而如果仅仅根据一个字段作为查询条件进行查询是没有意义的,如查询某个版块下的所有的角色及用户,没有意义。

假设我们的用户表是下面这样的:

SQL code
CREATE TABLE [ tb_Users ][ EntityId ] [ int ] IDENTITY( 10000, 1NOT NULL[ NickName ] [ varchar ]( 64NULL[ AvatarFileName ] [ varchar ]( 128NULL, [ AvatarContent ] [ image ] NULL )



那么对于“查某个版块下具有某个角色的所有的用户”这个需求而言,我们需要的是关联上面这两个表,并且以SectionId和RoleId作为查询条件,查询出tb_Users表中的相关记录。

对于上面这个相对复杂的需求,我们该怎么利用Hibernate来配置映射关系并且该怎么样来设计对象呢?我相信Hibernate肯定可以,但估计要比我之前举的例子要复杂难懂的多吧。因为这里大概涉及到三个原子对象(Section、Role、User),并且这三个原子对象可能会两两进行组合,并且都是M:N的关系,并且必须要同时根据两个条件进行查询才有意义。

经过我的思考,我觉得之所以我们会觉得麻烦是因为我们把相当一部分业务逻辑都交给Hibernate来完成了,Hibernate帮我们做了非常多我们觉得很简单,而要告诉Hibernate该怎样做却是很麻烦的事情。其实tb_SectionRoleUsers表是一个关系表,在Hibernate中是不会有关系对象这个概念的,对象与对象之间的关系Hibernate可以帮我们自动维护,我们无需关心。而我们现在的需求就是想简单的将一个关系表和一个用户表关联,然后查询出符合条件的数据而已。

我觉得无论是表之间的各种关系还是对象之间的各种关系,都属于业务逻辑范畴,这些关系的实现不应该交给ORM框架自动完成,而应该由我们来自己完成比较好。

另外一个我发现的规律就是,数据库是关系型数据库,也就是说数据库里所有的表中存放的数据是没有直接关系的,可以说是扁平状(平行状)的,虽然有一些外键的约束,但我们不能认为两个表有外键约束就认为他们是一个包含的关系,最多是一个引用的关系。所以,表与表之间都是平行的关系,而非树状关系。但纯面向对象的思想则不一样,对象有包含或聚合的关系,可以说是一种具有多个跟节点的树状关系。这是两种完全不一样的存储数据方式。Hibernate的做法是,数据库这边没有做任何的妥协,对象这边也没做任何妥协,因此导致框架为了转换这两个存储格式完全不一样的数据必须要做非常复杂的映射;既然这样,我们为什么不同时稍微改变一下这两种数据格式呢,让数据库提供一些有关系的“东西”出来,比如建几个视图;而对于对象,不需要完全按照面向对象的思想来设计对象,而是也设计一些扁平状的对象出来,也就是说各个原子对象之间不明确知道它们之间的具体关系,当我们需要连接两个具有某种关系的对象时,我们可以像数据库的视图那样,设计一些组合对象出来,并且所有的这些原子对象以及组合对象也和数据库一样,是扁平结构。这些组合对象用于和数据库的视图建立映射关系。这样一来,对象和数据库两方都做了一些让步,但换来的结果是数据存储格式的统一,这样的好处就是ORM框架在也不用这么复杂难懂了。我觉得这样不是满好的吗?并且我觉得这样的设计到更有点像ROM了,因为我们的思路已经转到了以数据库为基准,然后稍微修改一下对象的设计,从而达到一个数据存储格式的一致。然后ROM框架基于这个前提,为我们提供一些实用的接口,为我们实现CRUD操作。相信这样的ROM框架的映射文件也是简单并且很好理解的。

好了,就这些吧,大家觉得我的想法如何呢?欢迎大家批评指正。

 

最后,基于我自己的这个想法,我经过长期的不断努力和改进,写了一个小型的ROM框架,思路完全和我上面说的一致。

大家可以看到对于任何数据请求,通过我的框架来处理,ROM配置文件以及对象设计或者业务逻辑是如何的简单。

 

下载地址:

http://www.cnblogs.com/netfocus/archive/2010/01/10/1643207.html


目录
相关文章
|
11月前
|
前端开发 JavaScript Java
Web.xml - Servlet与Filter的url-pattern
Web.xml - Servlet与Filter的url-pattern
95 8
|
6月前
|
XML 应用服务中间件 数据格式
Servlet的urlPattern配置及XML配置Servlet
Servlet的urlPattern配置及XML配置Servlet
73 0
|
存储 搜索推荐 关系型数据库
SpringMVC何时加载的controller里的mapping方法
疑问 其实我一直有一个疑问,因为我在跟自己写的controller的生命周期源码的时候,没有发现解析mapping的代码,然后我就在想,什么时候解析并加载的mapping呢??? 结果是一个新的类 RequestMappingHandlerMapping ,惊呆了,我的小伙伴
99 0
|
Java
JSP之两个Filter
JSP之两个Filter
186 0
|
Java Apache 容器
Servlet mapping specifies an unknown servlet name Action
启动Java Web项目时提示错误:Servlet mapping specifies an unknown servlet name Action 完整错误信息如下: 严重: A child container failed during start java.
3417 0
|
Linux C++ Windows
|
JSON Java 数据格式
Servlet、Filter
加载顺序是:context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。
1359 0
|
Java 数据库连接 数据库