O/R Mapping

XPO开发指南简要

 

当程序员在开发一个应用程序时,总是会处理一些数据,在很多情况下,你会想用某种方法来存储它们。有很多方法能够实现,例如纯文本文件,XML文件输入到数据库,甚至可以是一些外来的数据容器。但如果是处理需要高性能高可靠性查询的数据,那就一定要使用关系型数据库(例如Oracle,SQL Server)。

另一方面,我们还要考虑到应用程序开发所使用的编程语言。如今的语言通常都是面向对象的,就如.Net Framework中使用的语言,例如VB .Net,C#等等。在这些面向对象的语言中,很有可能建立现实的构造和程序构造的映射。在IT工程的分析阶段,可能会产生一个面向对象的模型,例如一个类图。所有在真实世界中我们想要处理的数据,都对应面向对象模型中一个或多个类。每一个项的特性,转换成一个特定类的属性(例如,Customer类的Name属性)。上面提到的面向对象模型,在VB .Net,C#或别的含有面向对象特征的语言中,可以实现,结果就是类集被实例化为对象。例如,你创建了一个Customer类的实例,并把这个对象的Name属性设置为“John Doe”,这样你就有了一个代表真实世界中的John Doe的Customer对象。

这些对象是存储在内存中的,所以如果应用程序退出或计算机关闭,这些对象就消失了。在大多数情况下,你大概想把这些对象存储在某个地方,使数据不会消失,例如存放在硬盘上。在.Net Framework中使用XMLSerializer和XMLWriter结合就能很容易的实现。具体做法是:把对象序列化为XML,然后将XML写到硬盘上。这个工作对存储数据来说很伟大,哪怕是复杂的对象树,也可以被序列化,然后从XML反序列化,重新成为对象。但如果你将大量数据存储在XML文件中,查询数据将会变得非常慢。例如,你想要在1000个序列化的对象池中找到John Doe顾客,你需要一个一个的读取,直到找到需要的对象。如果你需要高性能的查询大量的数据,在大多数情况下你就要使用关系性数据管理系统(RDMS),例如微软的SQL Server。在这样的系统中,数据存放在索引表中,互相都有联系;于是高性能复杂查询成为了可能。所以我们再一次需要将一个模型映射到另一个模型,这一次是一个面向对象模型映射到一个关系模型。这样做了之后,在我们的编程环境下使用对象模型,以及将真实数据存入一个关系数据库,成为了可能。这就是O/R Mapping系统(对象/关系映射系统)可以发挥作用的地方。一个O/R Mapping系统位于面向对象模型和关系模型之间,于是,它把对象转换成了关系型数据。

回复人: kailler2002(Kriss) ( 一级(初级)) 信誉:93 2004-08-16 12:42:00 得分: 0
 

hasty(飞雪)说得很对.O/R映射的目的就是面向对象的系统能够脱离关系数据库,使用程序员不必在数据访问上投入太多的精力.看过JAVA的HIBERNATE就知道,真正的O/R映射,在代码中操作业务对象(业务实体或类)就等于对数据库的操作.
至于存储过程,你是无法把它与系统中的某一对象对应起来的.如果在你的存储过程中加入了业务过程的话,那系统的分层就不是很清楚了.而存储过程中的事务是可以用其它事务处理来代替.
或者你的本意并不是想做一个O/R映射,而是一个数据访问层吧?

主  题:我自己搞了一个o/r mapping 方案,把我的一些设计思路贴出来大家评评
作  者:helloparker (暗水晶)

    首先,需要一个东东同数据库打交道,而且要管理所有的可持续化对象,我把它叫做container,在我的方案中,它就是ObjectMapping.ObjectContainer,嗬嗬,这个想法最初的来源就是j2ee之cmp(当然没有她强了)。这个叫container的object,负责根数据库交流,还要负责管理所有持续化的对象,如果你想让她帮你找到你心目中的mm,你得这样:
MM myMM = container.GetObject(typeof(MM),primarykey)(这是抄objectspaces的拉),然后,对你的MM随便做些什么吧...,最后,container.Store(),ok,你的mm已经持久化了。

    其次,持久化的对象。怎么持久化呢?反正我在从前,如果想要保存一个对象的数据,一定会写insert,delete,update的,好在现在不用了,因为我所有的持续化对象类都是一个叫做ObjectMapping.ORBase.ORObjectBase的家伙的子女,他是一个抽象类,从来没有实际的实例(人是上帝按自己的样子创造的)他们的基因里都有一种神奇的功能----只要你按照一定方式来使用这个子类(代码可以用visio直接生成),那么,只要注意对象本身就好了,比如mm.BoyFriend = me,mm.Kiss(me)...嗬嗬,剩下的事情都交给container好了,你对mm做的任何事情现在都已经是记录在案了,不要抵赖哦。

    再次,需要一个持久化对象的集合,来扶助管理对象,这个东东在我的方案里面就是ObjectMapping.ORBase.ORObjectCollectionBase.这个东东用法很简单,比如:
me.MMs.Add(pretyMM),me.MMs[99].Kiss(me),me.MMs.Remove(dragon):(。

为了方便大家接下来的阅读,我把我的方案中主要用到的几种关键技术简要介绍一下,如果你是高手,帮我检查一下有没有错误的地方,如果不太明白,那就多读两便。
1.反射
如果没有反射,我上面说的东西都是狗屎,反正我不想做那么复杂的东东。
以下是msdn上的说明:
    通过反射命名空间中的类以及 System.Type,您可以获取有关已加载的程序集和在其中定义的类型(如类、接口和值类型)的信息。您也可以使用反射在运行时创建类型实例,然后调用和访问这些实例
详情请见:ms-help://MS.MSDNQTR.2003FEB.2052/cpguide/html/cpcondiscoveringtypeinformationatruntime.htm

反射非常有用,比如在运行时我知道了一个对象的某个属性名字,然后想得到属性值,从前com中有后期榜定这么一说,在.net上面更简单---mm.GetType().GetProperty("BoyFriend").SetValue(mm,me,null),就ok了


反射中另一个很有用的东东是Attribute,

2.xml
嗬嗬,当今世界,搞computer的没几个人不知道xml把,不多说了。

3.弱引用
    这也是一个很有用的东东,试想这样一种情况,你有一个arraylist收集对各种各样对象的引用,后来,arraylist引用的东东超过作用范围了,GC本来可以很轻松的就把它当成垃圾处理了,可是,你的arraylist还把它当个宝贝似的留着,简直是浪费资源!如果你用了weakreference就好多了。
这里有弱引用的详细信息:http://www.csdn.net/develop/read_article.asp?id=19987


以下是我的设计了。

首先是ObjectContainer,这个东东在上面已经简要说明了,现在我确定一下它的职责:
1.同数据库打交道:
    crud(create,retrieve,update,delete)的活全是它的,够好吧。
2.管理持续化对象:
    既然你通过container得到了想要得东东,那么container要对它负责到底。需要的时候把对象持续化,在你不用以后还要负责清理垃圾(这个功能我用了weakreference哦)。

    以下是我定义的几个成员函数
    public ORObjectBase CreteObject(Type type,object primaryKey)
    顾名思义,这个函数用来创建可持续化对象(请注意,我没有用new来创建),以后想有新mm就用它了

    public ORObjectBase GetObject(Type type,object primaryKey)

    这个函数呢,是用来得到已经有的东西的,注意,只返回一个哦

    public ORObjectCollectionBase GetObjects(Type type,object condition)
    这个函数得到一个集合,凡是满足condition的东东都会被放在返回结果中,如果没有满足条件的结果,那么ORObjectCollectionBase.Count==0,比如ORObjectCollectionBase mms = container.GetObjects(typeof(MM),"世界上最漂亮 and 爱我"),估计,mms.Count==0 :<。

    public void SetRelation(object firstObject,object secondObject,string name,bool exist)
    这个函数用来设定对象与对象之间的关系比如SetRelation(me,mm,"lianren",true),现在mm是我的gf啦!

    另外,相信我,我绝对会让程序员很少用到以上的函数的(GetXXX例外,经常用它查询),程序员根本就不用关心SetRelation。
    还有一点,注意到primaryKey了吗?它是一个object类型,这就意味着,你的primaryKey可以是string或者是自定义类型(嗬嗬,还是比较强吧)

    最后还有一个重要方法public void Store(),没别的用,让大家都持续化而已。

    container主要的东东就是上面了,其他还有一些细节地方这里说了也没用,还是等用到的时候我来解释一下吧。  
=================================================================================
嗯,快下班了,准备回家接着写,下面写一点废话吧。

    我之所以想写这个东西,是因为以前用过ejb,cmp的,真的好方便,后来搞.net,发现没有这个东东。于是一时冲动想自己搞,不管怎样,最后终于搞了一个比较类似得出来了。现在这个东西基本上还能用,自己也觉得很高兴,不过觉得还有很大改进余地。我算是井底之蛙吧,这个东东也许做得不好,但我还是想把它给贴出来,希望有比我做得更好的能做我的老师,也希望能跟更多.net们叫个朋友,大家共同进步。
    还有,对代码有点说明,由于很忙,没有时间写很多的注释,所以想看的朋友请谅解一下,后面我回一一作解释的,另外代码中难免有不对的地方(有些可能是设计上的问题)希望大家能帮忙提醒我哦。

哦,我的email是helloparker@163.com
接下来讲一讲ORObjectBase,这个类是所有需要可持续化的东东的基类,它是一个抽象类,永远不会有实例(有人见过上帝吗?),可是,他有一些非常奇特的特性,让它的后代们能够很容易的持续化。他有下面这几个主要方法:
protected object SetValue(string property,object theValue)
protected object GetValue(string property,object theValue)
嗬嗬,这两个方法要放在一起说明,他们可是天生的一对,前一个方法用来设置属性值,后面一个用来得到属性值(嗬嗬,是废话吧),请看一下,它们都是用protected修饰的,会怎么样呢?那就意味着自己的后代可以继承这个方法,而别人不能访问!试想一下,如果用这两个方法代替一般的get,set方法,那么,你能够做些什么呢?只有自己知道吧。
反正我就是利用了这两个方法来达到的持续化效果。比如说,你有一个类MM,需要持续化,那么
 public class MM:ORObjectBase
 {
  public MM(ObjectMapping.ObjectContainer container,StatusEnum status):base(container,status)
  {
  }
  //这个是主键
  private string _id;
  public string ID
  {
   get
   {
    return (string)GetValue("ID",_id);
   }
   set
   {
    _id = (string)SetValue("ID",_value);
   }
  }

                  //这个是关系字段
  private Boy _boyFriend;
  public Boy BoyFriend
  {
   get
   {
    return (Boy)GetValue("BoyFriend",_boyFriend);
   }
   set
   {
    _boyFriend = (Boy)SetValue("BoyFriend",_boyFriend);
   }
  }
 }

明白了吗?奥妙就在于GetValue以及SetValue这两个方法里面!至于为什么GetValue以及SetValue要做成这样的形式,我后面再解释,现在还是不用把注意力放在这里吧

好了,ORObjectBase就这么简单,只要你继承了他,并且使用GetValue,SetValue,你的类就可以持续化了,没有什么好担心的。

下面该轮到ORObjectCollectionBase了,同样,它也是一个抽象类,如果你的对象之间存在一对多,或者多对多关系的话,就应该用上他了,他会帮你打点一切的。你先要继承他:
 public class MMCollection:ORObjectCollectionBase
 {

  public MMCollection(ObjectMapping.ObjectContainer container,ORObjectBase tank,System.Collections.ArrayList objs):base(container,tank,objs)
  {
  }
 }
如果你很懒得话,这样就可以了,如果你很勤快,重写this索引,让返回类型变成MM,就这样就好了,什么都不要了。
ORObjectCollectionBase实现了IList接口,也就是说你可以用foreach访问,或者把他帮顶到DataGrid上面还有。。。IList能做的,都能做(嗬嗬,接口就是好)

还有,你需要为你的持续花类指定这个集合,这样,在返回多个对象的时候,container好知道应该使用哪一个集合类(如果是懒人,指定统一个集合类算了,大不了在索引的时候自己做强制类型转换)嗬嗬,现在,想到用什么办法指定集合类了吗?我是使用属性来指定的,像这样:
[CollectionClass(typeof(MMCollection))]
public class MM:ORObjectBase
{...
 ...
更正一下上面的:
public class MM:ORObjectBase
 {
  public MM(ObjectMapping.ObjectContainer container,StatusEnum status):base(container,status)
  {
  }
  //这个是主键
  private string _id;
  public string ID
  {
   get
   {
    return (string)GetValue("ID",_id);
   }
   set
   {
    _id = (string)SetValue("ID",value);//原来是_id = (string)SetValue("ID",_value);
   }
  }

                  //这个是关系字段
  private Boy _boyFriend;
  public Boy BoyFriend
  {
   get
   {
    return (Boy)GetValue("BoyFriend",_boyFriend);
   }
   set
   {
    _boyFriend = (Boy)SetValue("BoyFriend",value);//原来是_boyFriend = (Boy)SetValue("BoyFriend",_boyFriend);

   }
  }
 }

呵呵,上面已经把三个主要类的用法以及功能介绍了,现在应该是把思路整理一下的时候了。我的想法很简单:
每个实体类(借用ejb的术语哦)的字段大致上分为这么几类:
1.表示实际数据的字段
    这些字段是映射到数据库中具体的表中相应字段的,也就是实际上持续化的字段。这些子段应该都是些基本类型的。(或者,也应该可以是自定义类型?我想过,但是没有实现,因为很懒)

2.主健字段
    这些字段其实也应该跟第一种字段一样的,不过它可以唯一的标识出一个对象。另外,为了让程序员可以有更加灵活的余地,我把主键字段设计成了既可以是基本类型,又可以是自定义类型。

3.关系字段
    类与类之间的关系可以是one2one,one2many,many2one,many2many,这种字段可以是ORObjectBase(one2one,或者many2one的many一边)或者ORObjectCollectionBase(many2many或者many2one的one一边)类型(如果想把one2one当成many2many的特例来处理我不介意哦)

4.其它类型的字段
    这些字段可以起一些辅助作用,基本上我不关心。

好了,有了这些类型的字段以后,该想想怎样达成我的目的了。在GetValue以及SetValue的时候,实际上首先是要区分这几种类型的字段,然后分为不同的情况处理,或者修改数据(当然,数据应该是存放在内存中的,直到你调用container.Store()),或者是得到另外一个持续化对象或者对象集合。而且,要让所有的属性都通过GetValue,SetValue来访问,怎么让他们两个以一当百呢?用反射!还要知道访问的是哪个属性,所以他们两个的第一个参数都是string行,也就是访问的属性的名字!
再看看container,按前面说的,他可以管理对象何时应该持续化,决定怎么持续化并且实施持续化,因此它必须对每个对象都了如指掌。怎么办呢?我想,比较简单的办法就是让container自己来创建对象,这样就可以解决问题了(呵呵,想想ejb的方式,也有点这种味道哦)。

但是我还需要一个辅助角色来帮助container以及GetValue,SetValue在运行时知道持续化对象的属性对应到数据库中的哪个表的哪个字段上面或者对象之间的关系是怎么样的。

这个最佳配角嘛,就是ObjectMapping.ORBase.ORMap,它里面包含了各个持续化类的信息。而每一个持续化类的信息由ObjectMapping.ORBase.ORTypeInfo来代表,它包含了详尽的信息,
比如,主键是什么类型、主键的名字是什么、字段映射到数据库里面哪个表的哪个字段、跟其他类之间有些什么关系等等。而每个映射字段的详细信息又是由ObjectMapping.ORBase.ORFieldInfo来表示(包括数据库字段名,对应的类字段名以及数据类型)关系字段的信息则是由ObjectMapping.ORBase.ORRelationInfo来表示的(包括关系的另一边的类型信息、关系表,两个类的主键在关系表上面的映射等等)
    呵呵,还有一点要说明的是,我把每一个关系字段都当作了一个查询(呵呵,ejb上面有专用的对象查询语言哦),虽然我很想实现ejb-cmp上的那种查询语言,可是苦于没有时间,所以就偷懒省略掉了(其实要加上也很容易拉,只要学编译原理的时候没有睡觉,呵呵)既然有查询,那么当然有一个对象来代表查询的(oo万岁),就是ObjectMapping.ORBase.ORQuery,看到GetObjects的第二个参数了吗?声明是:
object condition
    其实,那是障眼法拉,它只接受一种类型,就是ORQuery(呵呵,为了以后好扩展留下的哦)。这里,把GetObject 跟GetObjects比较一下,GetObject的第二个参数也是object类型,不过,它是primaryKey不是condition!
    ORMap虽然是配角,可是没有他,一定会在o/r的迷宫中迷路的!下面把ORMap以及跟他相关的类之间的关系简要描述一下。可惜,csdn里面不能贴图,要不我把他们之间的关系用一张uml静态图来表示再清楚不过了,下面只好用文字描述了,讲得不好请见谅阿,如果术语有错误的话,麻烦指正。ORMap中有一个HashTable,包含了所有持续化类的ORTypeInfo(用哈西表比较容易检索,如果这样有什么弊病,麻烦告诉我)他对外公开的也只有这个HashTable。而ORTypeInfo则是由ORQuery、ORRelationInfo、ORFieldInfo聚合而成的(这里有点小遗憾,下面讲)特们都对应着ORTypeInfo中的一个HashTable(还是查找方便,不过HashKey的选取有些头疼)

    呵呵,这个ORMap还不错吧,不过我觉得对他很不满意,原因就是在设计这个东东的时候,老想着学ejb那样把信息保留在xml文件中,于是对类的设计时老是想着怎么对应到怎么跟xml文件配合,反而忽视了其本质----一张图!这也算是个教训吧,今后的设计一定避免出现这种情况。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页