在我们的系统中,存在大量的需要进行持久化存储的对象,这些对象可能是各种各样的业务单据,也可能是我们的系统配置信息等。另外一些属于内存中使用而不需要进行持久化存储的对象,不属于我们的讨论范围。而在支持对象序列化的语言比如C#,Java中,我们可以将这些对象序列化到磁盘文件或者直接保存到关系数据库中。其中关系数据库是我们最主要也是最安全的选择。
在对数据库中的业务数据进行各种操作的时候我们发现,我们进行的大量的Insert、Update、Delete操作,除了其SQL语句根据不同的对象有不同的变换之外,其主要流程存在很多相似之处。于是人们就想通过一个东西来实现这些相同部分的操作的自动化,而那些不同的流程,操作再做特殊处理。这样我们就可以把主要的精力都集中在那些个性化的,特殊的流程操作上。这就是ORM产生的根本原因。
在Java的世界里,ORM框架的应用比较早,也比较成熟,比如Hibernate等,而在.NET世界里,起步就相对晚的多。但是由于担心使用开源的ORM框架导致项目更为复杂(因为这些开源工具虽然很好,但是各个项目总是存在自己特殊的地方。所以目前不敢用)。但是我们确可以自己设计开发些小型的,尽量符合自己项目的ORM。
可是不论怎么样,我们都需要对ORM有一定的了解:
一、对象的继承结构:
在支持OO的语言中,继承是最重要的概念之一,所以我们的ORM也应该对继承作出相应的支持。一般来说,对于ORM世界中的继承有三种模式:
1、一个继承树映射到一个表(ONE_INHERITANCE_TREE_ONE_TABLE):
即将具有相同父类的所有类都映射到一个表中,这些类属性映射的并集组成了这个表的所有列,在这种情况下,只需要对最底层的类进行映射。如下面一个类结构:
在上面的类结构中,父类有属性Property1和Property2,而子类Child1有属性Property3,子类Child2有属性Property4。
所以如果采用ONE_INHERITANCE_TREE_ONE_TABLE映射模式的话,数据库中只有一张表。
类属性 | 数据库表字段 |
Property1 | Field1 |
Property2 | Field2 |
Property3 | Field3 |
Property4 | Field4 |
但是这种模式存在大量的属于冗余,对于Child1,由于没有Property4属性,所以字段Field4是冗余字段。同样,对于Child2,Field3是冗余字段。但是这种模式的优点的简单。
2、一个继承路径映射到一个表(ONE_INHERITANCE_PATH_ONE_TABLE):
同样一上面的类结构,数据库中将存在两张表,分别对应于Child1和Child2.
Child1对应的数据库表:
类属性 | 数据库表字段 |
Property1 | Field1 |
Property2 | Field2 |
Property3 | Field3 |
Child2对应的数据库表
类属性 | 数据库表字段 |
Property1 | Field1 |
Property2 | Field2 |
Property4 | Field4 |
这种模式的优点是没有数据冗余,但是缺点也是很明显的,那就是,当我们想根据Parent来查找满足Parent的Child1和Child2的时候,就必须同时对两张数据库表进行查找,当继承数横向很大的时候,这种查找将导致大量的性能下降。
3、一个类映射到一个表(ONE_CLASS_ONE_TABLE):
对于上面的类结构,对于的数据库表如下。
这种模式下,每个类对应于一个数据库表,其中字表和父表通过ID进行关联。当然这种方式的优点是数据冗余小,但是缺点是,当继承关系很复杂的时候,我们构造SQL也会变得相当复杂,从而导致修改变的也很复杂。效率也会降低。当然了,如果一个类没有父类也没有子类哪么上面三种模式都是一样。
二、对象的组合结构:
对象的组合结构是指一个对象中包含若干不同类型的子对象,比如人(Person)对象包含了手(Hand)对象,头(Head)对象等。这些都是ORM应该考虑的问题。
三、对象状态:
对象一般包括如下几个状态:
1、还没有和任何数据库数据关联的对象,比如刚new出来的对象,此时对象中没有任何实际数据。
2、对象初始化并且设置了属性值,但是还没有被保存,此时需要使用ORM的Insert功能。
3、从数据库中读取出来的数据对象,并且已经修改,此时对象对应于数据库中某条记录。
4、数据库中记录已经被删除了的对象。
上面几种状态需要在我们的ORM中能有所体现。比如对于第一种状态,我们需要对其设置属性值,然后转换成第二种状态。在ORM中执行Insert操作。对于第三中状态,我们需要执行Update操作,第四种状态我们应该进行错误提示。
四、ORM中的事物处理:
事物处理是每个底层框架都应该考虑的问题,即使不能提供自有的事物处理模型,至少也要能够提供能够进行事物处理的接口等。
.NET中事物处理有两种方式,一种是使用COM+,通过使用TransactionScop来实现事物处理,这种方式性能有一定的下降,但是整个代码显得非常优美。另外一种方式是通过SqlTransaction来实现事物处理。由于我曾经被COM+弄的很惨,所以我这里推荐使用SqlTransaction。
五、O/R Mapping的一般做法:
要实现对象和关系之间的映射,我们需要定义映射规则,也就是类的属性和数据库表字段之间的一一对应规则。比如上面所说的Child1.Property3对应数据库表中的Field3字段。在.NET中我们可以通过自定义Attribute来实现。好像也能够通过配置文件来描述映射规则,但是我这里仅仅讨论的是Attribute的形式。