我们为什么使用ORM?

博客园在推广ORM方面的确做了很大的贡献,很多的程序员开始使用ORM,不用写SQL的喜悦让他们激动不已,可是好景不长,他们很快发现众多的烦恼一个接一个的出现了。

很遗憾,我并不打算在这篇文章中解决这些问题,因为的确存在这些问题,而且目前没有完美的解决方法。那么既然这样,我们为什么要使用ORM呢?难道真的是为了不使用SQL吗?

还是要看O - R ,我们为什么要将关系型的数据转化成Object的方式,DataSet的方式难道不好吗?和数据库的表现还是很一致,又简单又方便,为什么先辈们要兴师动众的转化为Object。
我们知道,Object是可以继承的,是可以使用接口的,而Relation没有这个概念。就是因为这一点,我们将实体设计成Object方法,从而获取了大量的优势。
例如:我可以在程序中检测实体是否支持IVersionObject接口,如果支持,我们将自动做版本控制,而如果你给我一个DataSet,那我将无法检测(不要告诉我检测是否存在Version字段)。通过这个特性我们将可以自动化处理很多的事情。
又如,我设计了一个单据实体的基类,包含了SheetCode、SheetDate等等字段,然后我的OrderSheet继承自SheetBase,他们将自动获取到这些标准的字段,而且我的基础类可以自动帮助我处理很多统一的规则,使程序更加稳健和统一。而这个Relation的东西是非常难做到的。

市面上有很多的ORM系统,鱼龙混杂,事实上,相当多的系统根本无法利用Object的继承特性,他们还是一堆的如何做一对多、多对多的概念。根本没有了解到ORM的精髓就做出来。

在这里我需要解释几个误解:
1、ORM使我们摆脱了SQL,但并不代表我们不再使用SQL,事实上,复杂的查询和报表我仍然推荐使用SQL,良好的系统应该可以兼容以前的方式;
2、微软在表模型(Relation)上花费了无数的精力,所以目前Relation的一揽子解决方案是最完整,最好的。但我们看到,微软在.NET 2.0中对Object方式的绑定支持更近了一步,随着LinQ、XAML等很多后续技术的发展,相信领域模型(Object)的完整解决方案将更加完整;
3、ORM更适合复杂的系统(这里使用复杂,而不是大型),而不是小的系统,因为这样的系统要求建造速度快,系统稳定,他们的业务规则异常的复杂,但他们对系统的性能要求并不是很高(相对电信这样的性能要求)。
 
 
 
 
 
 
 
 
二、我需要什么样的ORM(对象持久层)
 
首先声明,标题中是“我”,不是“我们”,别人希望如何我不太能够帮着下结论。

一个可以打动我的持久化层(Persistence Layer)或者说OR/M(Object Relateion Mapping)应该具备哪些特征呢?

1、简单,尽可能地简单
没必要为了我偶尔用到的功能花费太多的精力,CRUD满足之后,关于Relation如何处理的问题,我觉得尽可以不必那么讲究,一定要那么OO吗? 一定要 order.items这样去访问order的明细吗? 我觉得太可不必,为了实现这种功能,一是我必须再设计Entity类,以决定有哪些OnetoOne、OnetoMany, ManytoMany的关系,然后会定义一个类似这样的很OO的class出来:
class order
{
  .....
  customer _customer;
  entitylist<orderline> _items;
}

看上去真的很帅,但是我必须去维护它,因为不是所有的关系都需要这样定义,lazy loading并不能让人就可以放肆地定义一个很“胖”的类出来。

如果有人说我可以先class,再使用schema工具生成数据库,那么很不幸,我们没有共同语言了,起码我现在对这种方式不感兴趣,而且我也从来不这样做,像 如何让union all构造出来的view利用到索引里所说的一样,我会因为数据库增加一些不相关的列,也会因为性能而再增设冗余,用class的方式去思考的话,我很难知道最终的数据库要做多少修改。如果仍然用“表”的方式去先构建“类”,那么我觉得这是一种掩耳盗铃的行为。顺便说一句,我暂时对domain object还不是很感兴趣。entity就是用来存储数据的,其它的事千万别让它去承担。

那么我希望如何处理relation呢? 非常简单:
entitylist<orderline> items = pl.getList(order, typeof(orderline));

那么如何生成filter呢? 命名规则+代码生成,用order的主键id的值,以及用orderline的order_id列,构造select ... from orderline where order_id = order.id

2、透明,让我知道所发生的一切
  
我不希望在保存一个或一组entity时,会发生我并不知道的级联更新,或者有些认为我没有dirty flag而不做任何更新,因为有时候一个update t set f = f 并不是什么活都不干的,当我要retrieve时,别因为缓存中已经有了就不从数据库加载了,因为可能我确实需要最新的数据,或者把数据缓存而不提交,这样我的另一个client会因此而无法即时获取数据,数据库的连接也要让我知道,是使用了连接池还是立即释放掉了,什么时候会开启事务,什么时候会提交事务,这些,我统统要知道。

有人会有意见了,ORM不就是为了隔离数据库么,你要知道那么多鸡毛蒜皮的小事干什么?

我只能说,这些东西如果我不知道,那么我就很难写出对数据库优化的处理。而需要这个的话,仅仅是可以trace generated sql是不够的,我希望是当告诉持久层去买烟时,它不要很机灵地顺便买个打火机回来,尽管很多时候这个机灵是很讨好的,但有时候可能会因此给我带好不必要的麻烦。

这里透明的意义就是可控性,当我要它做的时候再去做。

3、可测试性,减少重构或者维护的工作量

这个重要了,假如说我要构造一个查询,怎么办? criteria.add(a.ID like 'a',LogicOperator.ADD)? 这种方法又似乎很OO了,但是当要构造一个
(a and((b or c) and c)这样的条件时,写出来的语法可能连你自己都搞不清楚之间的关系了, 而且如果再加上join,再加上select fileds,会是什么样子?相当于原来SQL的语法树了,假如再来一些,计算列,case 判断,group,having,order by, exists... 天啊,受不了,还是用VIEW吧,可是view还得处理filter的问题,那么,换做Store procedure吧,传递的参数怎么办?用
(@a is null or a = @a)这样么? 再定义那些parameters,这个SP写得就比较难看了,再多来点if else的处理吧,SQL脚本就更长了...

一个查询或者复杂的更新,如何是最易于理解的? 就是SQL,要用对象化来转述它,只能让人看得云里雾里,ORM要不要去处理这些事情?我觉得要。但是就是最后的实现方式一易于理解,二要可验证是否正确,三要易于维护。

用cirteria构造,难于理解,也无法完全验证;用SP,难于维护,当加一个filter时,又要去修改sp对应的CLASS,如果不用sp<->class,那么关于参数的验证又无法保证了,因为可能有个地方你调用了exec sp @a,@b,@c,但后来这个SP可能参数定义,参数位置,参数个数都改变了,原来的调用就隐藏了一个BUG了,如果测试做得足够好的话当然没话说,但你真能做到100%的覆盖用例?要知道还有大量的if else存在。

那么我现在是如何做的呢?很简单,将SQL定义和代码分离,你放哪都行,怎么加密随便你,SQL的写法是这样的:
select a,b,c from t where #x# and #y# or z = :z

#x#是一个filter,当没有fitler定义给它时,它会用1=1替换,:z是参数。
这样有什么好处? 首先这条语句是可检验,当我用xml同样定义了filter后,我可以将对应的filter替换,并用相应的值替换:z,然后我就得到一个完整的SQL语句,这个SQL是可以提交到数据库去检验的。那么如何设置参数的实际值呢? 还是通过命名规则来映射,这个值会来自我程序内部维护的一个data store。

4、性能,瓶颈不能发生在这个地方

虽然说数据库执行SQL的时间会远大于ORM层处理和生成SQL的时间,但我的要求就是生成的SQL千万不要和手写SQL相差太多,对于批量的操作,一定要尽可能地快,有些批量的更新要给我一种特殊的操作,比如说对order的所有items设置一个flag,就没必要让我去遍历list,再一个个设置,再提交了,让我在一行代码写完这个处理,并且生成update orderline set flag = 1 where orderid = @id这样的SQL。

而这点和第2点有点重合了,只有当可控性强了之后,才有可能去避免性能问题。

那么对于集合操作呢? 我永远不需要items[3]这样去访问第3条数据,我永远不会加载一批数据,却只为了修改其中的几个,也就是说order.items对我来说没有意义,我会如何操纵这些集合数据呢?

大概是这样的:
C(create):
list = new list();
list.add(item);
pl.addUpdatetoQueue(order);
pl.addUpdatetoQueue(list);
pl.commit;

R(Retrieve):
list = pl.getList(order, typeof(orderline)

U(Update):
list["ABCDE"].price = 1;  // 主键为abcde的orderline

D(Delete):
pl.delete(order, typeof(orderline));  // 如果有级联删除就不用了
pl.delete(order);

这里持久层很像一个Util或者是Helper了是吧? OO的味道似乎完全没有了,但,这就是我想要的东西。 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值