• 浅谈ORM


    面向对象有它的本质要求。如果你的编程仅仅是“基于对象”而不是“面向对象”的,虽然你也能收到一些“代码封装成类型”的好处,但是你其实很难用到真正深入的面向对象软件工程设计技术。

    面向对象ORM非常重要,堪称面向对象软件工程技术的核心。但是,有很多ORM只是“基于对象”的,不是“面向对象”的。面向对象的ORM应该与面向对象的软件工程技术一样,首要问题是:解决基于对象的结构化技术(如今所有顽固坚持结构化技术的人都使用OOPL语言来说明自己的理论,因此OOPL不等代表OO的水平)与基于多态的面向对象技术的“阻抗不匹配”现象。

    套用面向对象技术的本质特种,面向对象ORM应该有以下本质特征(下面是用了熟悉的c#的术语):

    1. 自动维护类型定义。我们把一个对象“丢给”面向对象ORM,它就应该自动分析对象的Field、Property。如果在使用一段时间之后我们的对象类型定义改变了,原来的数据应该仍然能够回复为新的类型的对象,只不过有些新增字段成为默认值。例如:

         User u=null;
         //todo: 产生u实例
         using(var db=Domain(数据库连接))
         {
             db.Save(u);
             db.Commit();
         }
    db事先并不知道“User”这个类型,它应该“临时”反射User类型并记录下来。那些要求我们依据关系数据库的数据字典来写class代码的想法可谓“居心叵测”。

    2. 标识唯一性。至少在同一个进程中(也就是在同一个ORM环境中)两个不同的查询,如果逻辑上应该有相同的对象返回,那么ORM就应该体现这个规则。例如有一个过程是“保存工单记录”,工单中有字段记录负责评估工单的产品经理,另外有一个过程是“查询某个项目组的产品经理”。看上去这两个过程没有紧密的逻辑联系,但是他们有数据联系。如果一个地方建立个一个产品经理对象并调用了第一个方法,另一个地方随后调用了第二个方法,那么对于同一个产品经理,第二个方法不应重新建立新的内存对象,而应该返回第一个方法所使用的那个对象(除非调用第二个方法时第一个方法使用的那个对象已经被GC释放了)。

    显然,你的ORM应该有自己的缓存机制来维护对各个对象的弱引用。缓存并不是仅用来提高查询速度的,还有逻辑意义上的用处。

    3. 继承性。如果上述例子中的变量u被实例化为一个从User类继承的“系统管理员”类,数据库当然应该可以保存,并且在随后“查询系统管理员”操作中可以正确返回系统管理员的全部字段。显然,子类和父类在数据库中肯定要分开成不同的类型(或者叫做“表”)。

    例如假设Domain数据库类型的Query<T>返回所有T类型对象的一个QueryProvider(Linq定义的):
         User u=null;
         u=new 系统管理员(){姓名="大宝"}; //已经设置(例如使用一个Attribute在class中声明)“姓名”在数据库中是唯一的。
         using(var db=Domain(数据库连接))
         {
             db.Save(u);
             db.Commit();
             var result=(from u in db.Query<系统管理员> where u.姓名=="大宝" select u).FirstOrDefault();
             //todo: result.Print();
         }

    4. 多态性。

         User u=null;
         u=new 系统管理员(){姓名="大宝"}; //已经设置(例如使用一个Attribute在class中声明)“姓名”在数据库中是唯一的。
         using(var db=Domain(数据库连接))
         {
             db.Save(u);
             db.Commit();
             var result=(from u in db.Query<User> where u.姓名=="大宝" select u).FirstOrDefault();
             //todo: result.Print();
         }
    注意result的定义类型是User而不是“系统管理员”。多态让我们写出“引擎式”的代码,这个引擎拖动的车子是虚构的、通用的。ORM应该支持OO的这个本质要求。

    5. 网状关联。ORM应该隐藏面向对象数据库与关系数据库在处理关联方面的差别。

        假设User中有一个“配偶”字段,并且有一个“情人”集合,查询“系统管理员的配偶和情人”方法可能这样写:

         User u=null;
         u=new 系统管理员(){姓名="大宝"}; //已经设置(例如使用一个Attribute在class中声明)“姓名”在数据库中是唯一的。
         //todo: u.配偶=......
         using(var db=Domain(数据库连接))
         {
             db.Save(u);
             db.Commit()
             var result1=from u in db.Query<系统管理员> select u.配偶;
             //todo: result1.ForEach(p=>{p.Print();});
             var result2=(from u in db.Query<系统管理员> select u).SelectMany(u=>u.情人);
             //todo: result2.ForEach(q=>{q.Print();});
         }
    如果你使用面向对象数据库,那么从User到它的配偶之间以及每一位情人之间的存储关联是“一步到位”的,而不是像关系数据库那样需要使用inner join。因此,面向对象昂数据库(理论上)应该比关系数据库速度快至少10倍。

    不应该强迫对象类型之间关系模仿关系数据库的“外键”来建立,应该使用上面的自然、OOPL原始的形态。这样,如果你的ORM的底层是关系数据库,例如是SQL Server,那么你的QueryProvider(Linq)实现就不得不将关系翻译为关联操作。

    6. 延迟加载。显然,当我们查询一堆User出来之后,它的“七姑八姨”等关系对象不需要加载到内存里(否则这个加载操作就太可怕了),但是当我们访问关联对象时应该自动从数据库中加载到内存。对于引用单个对象和引用集合对象都是应该这样的。

    7. 级联更新。如果我们实例化一个User类型对象,并且给它的“配偶”赋值,那么在保存这个User类型对象的时候就应该自动保存(新增或者更新都叫是保存操作)它的配偶,而不需要我们在程序中去写保存配偶的代码。如果我们查询出一个User,他有很多情人,我们用程序向他的“情人集合”里插入一个情人或者删除一个情人,然后保存他,那么ORM应该自动也去保存这个情人或者删除它与这个情人的数据库内连接,但是不需要重复保存那些没有动过的情人对象。

    原文转自:http://topic.csdn.net/u/20080227/12/aeeec383-2def-48b9-8bab-336926f1d33b.html

    相关讨论:http://www.cnblogs.com/barton131420/archive/2007/01/07/613955.htmlhttp://topic.csdn.net/u/20090320/10/2742e3df-d49a-4402-8dc3-571d69676646.html

  • 相关阅读:
    二进制,八进制,十六进制,十进制之间的换算
    14简化路径(71)
    13字符串解码(394)
    12 反转每对括号间的子串(1190)
    11 使括号有效的最少添加(921)
    10 K 个一组翻转链表(25)
    9 从链表中删去总和值为零的连续节点(1171)
    8 链表中的下一个更大节点(1019)
    7两两交换链表中的节点(24)
    6 奇偶链表(
  • 原文地址:https://www.cnblogs.com/jzjblog/p/2654512.html
Copyright © 2020-2023  润新知