首先声明,标题中是“我”,不是“我们”,别人希望如何我不太能够帮着下结论。
一个可以打动我的持久化层(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
}
看上去真的很帅,但是我必须去维护它,因为不是所有的关系都需要这样定义,lazy loading并不能让人就可以放肆地定义一个很“胖”的类出来。
如果有人说我可以先class,再使用schema工具生成数据库,那么很不幸,我们没有共同语言了,起码我现在对这种方式不感兴趣,而且我也从来不这样做,像如何让union all构造出来的view利用到索引里所说的一样,我会因为数据库增加一些不相关的列,也会因为性能而再增设冗余,用class的方式去思考的话,我很难知道最终的数据库要做多少修改。如果仍然用“表”的方式去先构建“类”,那么我觉得这是一种掩耳盗铃的行为。顺便说一句,我暂时对domain object还不是很感兴趣。entity就是用来存储数据的,其它的事千万别让它去承担。
那么我希望如何处理relation呢? 非常简单:
entitylist
那么如何生成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的味道似乎完全没有了,但,这就是我想要的东西。
正文
我需要什么样的ORM(对象持久层)2006-07-24 14:32:00
【评论】 【打印】 【字体:大 中 小】 本文链接:http://blog.pfan.cn/Csharpsky/16790.html
阅读(5016) | 评论(2)
版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!
评论