对象关系映射
AgileEAS.NETORM并没有采用如NHibernate中映射文件的文件的模式,而是采用了直接硬编码的模式实现,ORM体系设计采用了属性/列>数据对象>数据集合(表)的结构:
最基本的思路是一个记录/实体(IEntity)映射一条记录,一个实体包括若干属性/列(Colunm),而一组IEntity和一组Colunm组成一个数据表对象(IEntity),用于对象一个表、视图、或者一个查询结果,下面我涉及到的类、接口介绍一下:
Column
相当于一个数据库表的一个列。组织于数据库与数据库表行中,由1-n个列组成一行数据或一个数据库表,包含了标题、名称、数据类型、数据库列名、大小、值表达式、是否自动增长、值、默认值等属性。这些属性方便用户在定义数据库实体类及表类时使用,在数据库属性类中,常规情况下,一个属性等同于一个数库列,但是,一个属性并不一定关联一个数据库列,也可以关联到一个函数或常量之上。
IEntity
实体接口相关于关系数据库中的一个数据库表行,把一个数据库表行映射库一个数据库记录。
数据库实体接口和数据表接口是ORM中最核心的一个接口,为什么说他是最核心的接口呢,他是ORM映射中的数据实体对象(Object)、他和关系数据库中的关系(表)进行直接的映射、一个数据库表行就是一个只有一个元素的关系(即只有一条记录的表)、数据表是数据库表行的一个纵行扩展。 数据库实体接口中实现了,和数据库表行映射所需求的属性集合,同时也提供了Refresh、Insert、Update、Save、Delete数据库持久化操作和一个CacheRefresh方法。
Refresh方法是数据实体对象从关系数据库表行同步自身的一个方法,他从关系数据库表中取出指定行数据,同步内在中的数据实现对象。
Save方法是数据实体对象根据把自己同步到关系数据库表中的一个方法,当数据库表中存在这条数据行是,修改数据库表中的这一行,如果数据库表行中不存在这一行,则向数据库表中插入这一行。
Insert方法不进行判读直接向数据库插入数据。
Update方法不进行判读直接更新数据库记录。
Delete方法是数据实体对象从关系数据库删除与数据实体对象映射的那一行数据。
CacheRefresh方法同Refresh只是不从数据库而是从缓存。
ITable
数据表相关于关系数据库中的一个数据库表,把一个数据库表映射库一个数据表,ITable中我们定义了Columns列集合、Rows行集合,同时也提供了Query、Save、Delete数据库持久化操作方法和缓存查询CacheQuery。
Query方法是数据表对象从关系数据库表同步自身的一个方法,他从关系数据库表中取出指定行数据,同步内存中的数据表对象。
Query方法在执行过程中,可以一次全部同步数据库表数据,也可以根据条件同步数据库的某一部分数据,在进行条件参数时,需求使用到查询条件对象(Condition)、查询条件单元对象(Element)和结果排序单元对象(OrdeElement)、由这三个对象组合成复杂的查询条件,通过Query方法查询指定条件的数据。
Save方法和Delete方法实现和IEntity定义中的有相似的功能,在此先不作介绍。
CacheQuery实现从缓存同步数据行。
在上面ORM的对象架构中,涉及到两个集合类EntityCollection、ColumnCollection在此文不做特别说明,详细请参考开发包中的类库帮助,下面我说一下ORM中的查询条件。
我们知道在进行数据库操作中要进行条件查询,我们把行、列、表都进行了对象映射,那么SQL条件怎么办,AgileEAS.NET中定义了三个类,查询条件(Condition)、组成条件的元素(Element)、排序条件元素。
Condition
条件类是ORM中的一个功能辅助类,他相当于开发人员在编号SQL语句的过程中所编写的一组查询条件。条件由条件单元组件,如果条件用于查询,在查询时,需要对查询结果排序,刚需求使用排序条件单元,以下是条件类及条件单元的结构关系:
上图为条件、条件单元(Element)、排序单元(OrdeElement)之间的类关系图,条件元素()由方法关联条件单元及排序单元,条件单元(Element)表示的是一个很简单的条件元素,比如 NAME = ‘james’, 排序单元(OrdeElement)也只是表示一个很简单的排序单元,比如:NAME或 NAME DESC,但是在我们进行的企业应用开发中,条件都是很复杂的,比如有这样的条件 NAME = ‘james’ And SEX = ‘男’ And Age < 16,这样的怎么么写,我们使用条件单元组成复杂的条件。这个条件我们使用ORM进行映射:
Condition condition = table.CreateCondition ();
condition.AddElement(new Element(“NAME”,”james”);
condition.AddElement(new Element(“SEX”,”男”);
condition.AddElement(new Element(“Age”,” 16”);
这样就完成了这个条件的定义,在条件的组合及定义条件的过程中,我们就有一个认识,条件并不是光有等值比较,还包括有很多其他的条件类型,同样,条件的组合不光是And 还有Or组合,下图是ORM条件映射中的两个辅助枚举,列举条件类型及条件组合类型。
ElementType
条件类型枚举,定义某个条件的类型,比如=,<,like之类的:
ElementType
在这些定义中,有一个特殊的条件类型,SqlCondition条件类型,我们在进行条件映射时,现实中的数据总是复杂的,有很多无法直接使用各种条件映射出,或者,通过单条件映射组件条件很复杂,我们可以直接使用SQL语句作为条件,在这个时间,就可以使用SqlCondition条件类型。它为我们保留了编写优质高效SQL语名的接口。
ElementCombineType
两个条件或者两条条件元素的组合方式,OR或者AND
/// <summary> /// Condition 类的数据元素(Element)之间的组合方法。 /// </summary> public enum ElementCombineType { /// <summary> /// And,两个Element 对象之间是以And 为组合条件。 /// </summary> And = 0x0000, /// <summary> /// Or,两个Element 对象之间是以Or 为组合条件。 /// </summary> Or = 0x0001 }
接口驱动的数据层
AgileEAS.NET平台一直在实践着接口驱动的思想,同时也在建议应用开发基于接口驱动,AgileEAS.NET平台实现一组实用并且简单的ORM,应用开发的数据访问层也就是基于ORM技术的数据访问层。
我们在应用开发中,经常会遇到同样的产品需要运行在不同的数据库系统之上,比台有客户需要运行在SQLServer之上的版本、有的需要运行在ORACLR之上的版本。
在这种情况下,采用接口驱动的数据访问层是一个不错的选择;定义一组数据访问层接口组件及其不同数据库类型的的数据访问层实现组件,业务实现依赖于数据接口层而与数据实现层解耦,运行期不同的数据库类型需求只需要修改系统的配置文件。
我来列举一个例子,我做也一个数据访问的例子,我们定义了一个名称为EAS.Exam.DAL.Interface的类库项目,包含了IIteminfo(服务项目)、IIteminfoList(服务项目表)、IProduct(产品)、IProductList(产品表)四个实体接口,一个管理这四个四体类具体实例库的IDALManager接口,由他来完成实体的实例化,下面看定义:
public interface IDALManager
{
IIteminfo CreateIteminfo();
IIteminfoList CreateIteminfoList();
IProduct CreateProduct();
IProductList CreateProductList();
}
同时在EAS.Exam.DAL.Interface项目中我们定义了一个DALHelper的类:
public class DALHelper
{
static string ComponentKey = "EAS.Exam.DAL";
public static IDALManager DALManager
{
get
{
return ContextHelper.GetContext().Container.GetComponentInstance (ComponentKey) as IDALManager;
}
}
}
DALHelper类是一个辅助类,提供了一个DALManager名称的静态属性,返回一个具体实例化的IDALManager对象,可能是SQLServer的实现也有可能是Oracle的实现。
我们在定义一个项目EAS.Exam.DAL.SQLServer分别实现这四个实体接品和DALManager接口:
class DALManager : IDALManager
{
#region IDALManager 成员
public IIteminfo CreateIteminfo()
{
return new Iteminfo();
}
public IIteminfoList CreateIteminfoList()
{
return new IteminfoList();
}
public IProduct CreateProduct()
{
return new Product();
}
public IProductList CreateProductList()
{
return new ProductList();
}
#endregion
}
Oracle数据库的实现EAS.Exam.DAL.SQLServer参考SQLServer的实现。
然后在系统配置文件(IOC)配置部分增加一个名称为EAS.Exam.DAL的对象定义,assembly和type根据需要的数据访问层实现进行配置。
SQLServer数据访问层:
<object name="EAS.Exam.DAL" LifestyleType="Singleton"
assembly="EAS.Exam.DAL.SQLServer" type="EAS.Exam.DAL.SQLServer.DALManager" />
Oracle数据访问层:
<object name="EAS.Exam.DAL" LifestyleType="Singleton"
assembly="EAS.Exam.DAL.Oracle" type="EAS.Exam.DAL.Oracle.DALManager" />
在基于接口驱动的数据访问层中,上面的例子中使用了IOC容器解耦,我们推荐应用开发使用这种模式,也支持程序员研究采用新的项目,比如抽像工作方法进行解偶。
工具的支持
在应用开发中,可以选择手工编码数据层代码,也可以使用AgileEAS.NET平台提供的数据对象设计器生成ORM及基于接口驱动的分层代码实现。
在AgileEAS.NET平台中,我们提供了一个集数据库设计、代码生成、DDL定义与一体的数据实体设计器:
在早期的AgileEAS.NET版本中包含一个代码生成器,用于根据现在数据库生成ORM代码,后来的思路是想介入项目的数据库设计环节,所以设计了这么一个数据对象设计器,提供一个数据表定义工具,在项目的数据库设计阶段(环节),使用数据表设计工具同时定义数据库和数据实体模型。
当定义好模型之后,可以直接生存数据库设计文档以及数据库定义语言DDL(数据能生成ORACLE和SQLSERVER两种数据库)。
以及基于分部类的ORM代码和基于接口驱动的DAL解决方案和项目,这些要说明一个问题是分部类,在应用开发中,我们对数据库的操作不仅仅是读取、更新、增加、删除这样的简单操作,在DAL层中还有配合业务逻辑的复杂数据库处理,这就需要程序员在生成的代码上进行修改,这就引发一个问题,当我们修改了数据定义模块之后使用工具重新生成代码之后就会覆盖原来的操作,为解决这个矛盾,AgileEAS.NET引入分部类的技术,将一个实体或表对象的实现分解为两部分,即与模块相关的定义部分和与业务相关部分,在第一次生成时,生存器生成写成的定义和一个空的业务代码文件,程序员在业务代码文件中增加业务处理代码,当模型修改之后重新生成时只覆盖与模型定义相关的代码文件。
此外,数据对象设计器还提供了基于现在数据库生成模型的反向生成工具,目前支持ORACLE和SQLServer数据库。
推荐的实践
目前,大多数的中小软件企业,都从事与数据库相关的信息系统的开发,项目中80-90%的业务都与数据库相关,也算是一种典型的数据驱动开发。
AgileEAS.NET平台针这对大规模数据交互的应用提供了从数据库设计到代码生成、业务代码扩展的一系列支持和实践。
定义数据对象模型
在完成应用系统或字系统需求进入设计阶段之后,AgileEAS.NET提供了数据对象设计工具两步完成数据库设计、数据对象定义;使用数据设计定义工具之类,项目设计阶段就不再需要独立的数据库设计步骤,数据对象定义工具输出数据定义语言DDL和数据库设计文档。
数据对象模型设计之后可以保存为扩展名为.SDM的数据定义模型文件,在定义数据模型这个环节,同步完成了数据库的定义(表名称、列名、数据数型、长度、小数、是否非空)、ORM实体的定义(实体名称、属性名称、类型、标题)以及数据库表和ORM实体的关系(列名--->属性名称)。
有有数据库表的元数据定义,我们就可以生成数据库设计文档、数据库定义语言DDL,有了ORM实体定义的元数据定义,在编辑阶段就可以生成ORM实体代码,实际上代码生成器生成的是基于接口驱动层的数据访问层代码。
在数据对象模型的设计过程中,可以选择设定模型的项目名称、标题、编程语言(C#、VB)、命名空间、输出目录等生成代码时需要的信息,如处图:
以及生成DDL、从数据库生成模型时所需要的数据库类型、连接信息等:
生成数据库文档
AgileEAS.NET平台提供的数据对象设计器可以根据设计好的数据对象模型生成数据库文档,文档格式如下所示:
生成数据库脚本
数据对象设计器可以根据设计好的数据对象模型生成创新数据库表的脚本,脚本中包含创建表、主键、表、各字段的说明注释信息,目录可以生成SQLServer和Oracle两种数据库的DDL脚本。
生成数据访问层代码
根据在数据库设计阶段设计好的数据对象模型生成数据访问层代码,所生成代码包含一个数据访问接口项目、一个模型设计时设定的数据库类型的数据访问层实现。
ORM对象设计器生成的基于接口驱动的数据访问层代码采用了分布类的技术,也就是把同一个实现接口或者实现分解成了两个或者多个文件,生成器是这样预设置的,把实体与数据库相关联的基础定义信息放在一个文件中,文件名中多了.Generator字符,与业务相关的放到一个文件中,同一个项目中,.Generator的文件放在个独立的文件夹Generat中,并且建议开发人员不要对此文件做任务的变动。
对于与具体业务有一定相关性并不是简单怕CRUD操作的业务扩展数据层代码,开发人员可以写在不带.Generator字符的文件,以保证重新生成代码之后与业务相关的程序代码不被覆盖。
链接
QQ群:116773358