Websharp.ORM 2.0实现原理[转]
原作者:徐芳波
序言
N层应用程序的开发,ORM工具的作用相当于数据访问层。如果不使用ORM工具,需要开发人员硬编码数据访问层。下面比较这两种做法的优缺点.
ORM的优点非常吸引人:不需要构造简单枯燥的SQL语句、对数据库进行I/O操作,这些都由ORM工具来解决,而且不易出错。但是缺点也成为ORM心中的痛,主要体现在两个方面:1,ORM不够灵活;2,ORM性能比较差,有时候会成为系统的瓶颈。
硬编码数据访问层优点是灵活、性能好;缺点是工作量非常重复,虽然可以使用代码生成工具来自动生成一些数据访问层的代码,但是数据模型是经常变化的,维护这些数据访问层的代码是很麻烦的.
通过上面的比较可以发现:ORM工具和硬编码数据访问层的优缺点是互补的,有没有办法写一个东西结合两者的优点,摒弃两者缺点呢?Websharp.ORM 2.0目的就是要解决这个问题。
Websharp.ORM 2.0原则和目的
简单、高效
简单包括两个意思,一是说Websharp.ORM 2.0实现原理很简单,其他人很容易了解ORM,本人认为ORM的这个特性是很重要的;二是使用简单。
题外话,以个人的经验,选择一个开源项目在自己项目中使用的时候,使用者对开源项目的实现原理包括细节了解清楚非常必要,如果项目因为开源项目出现问题无法控制则比较麻烦。Websharp.ORM 2.0把实现原理设计简单则要消除这个顾虑。
有所为、有所不为
不要希望ORM能做所有的事情,工具只能解决被抽象出来的东西,具体的说ORM工具的主要优点就在于减少重复的、基本的CURD操作,对于比较复杂的多表查询,ORM不能解决的话,或者解决的很丑陋,干脆就不去解决。比如下面下面的情形:
Websharp.ORM 2.0使用Entity作为实体数据的表现形式,目前很很难处理返回多个表记录的情况,那么Websharp则不处理这种情况.
本文开始说Websharp.ORM 2.0要解决性能和灵活的缺点,因为这个原因Websharp.ORM 2.0
实际上在灵活上只是部分的解决。Websharp.ORM 2.0主要的作用在于解决了性能问题
解决方法
一般的ORM和硬编码数据访问层的实现方式上主要的区别是,硬编码数据访问层每个实体都有专门的数据访问对象,ORM中所有的实体都使用一个数据库访问对象,Hibernate中的数据访问对象叫Session,JDO和Websharp.ORM中的访问对象叫PersistenceManager。Websharp.ORM 2.0的解决方法是使用动态编译技术为每个实体类都生成专门的数据访问对象(后文都叫持久化管理器),这样做的好处是一,实体结构变化,持久化管理器自动变化;二,持久化管理器是强类型的,一个持久化管理器只为一种类型的实体服务,不再有性能上的担心,设置还可以使用存储过程。三,软件开发完毕,大包部署的时候,可以使用动态生成的持久化管理器程序集,不再使用动态编译。
实现方式
动态生成的持久化管理器必须继承自PersistenceManager<T>,其中T表示实体类型。该接口的声明如下:
public interface PersistenceManager<T> : IDisposable { /* * 关闭一个PersistenceManager。 */ void Close(); /** * 判断一个PersistenceManager是否已经关闭了。 */ bool IsClosed { get;} /** * 数据库连接是否自动关闭,默认为真,当需要使用事务的时候需要设置为假. */ bool AutoClose { get;} /** * 获取PersistenceManager当前所处的事务 */ Transaction CurrentTransaction { get;} /** * 获取关联实体的PersistenceManager */ PersistenceManager<K> GetPersistenceManager<K>(); /** * 指示在默认的情况下,所有的操作是否忽略缓存。如果忽略缓存,那么,有的时候会存在一些不一致的情况, * 除非系统被禁止了缓存的使用 */ bool IgnoreCache { get;set;} /** * 将一个新的实体对象Insert到数据库中 */ bool PersistNew(T entity); bool PersistNew(T t, PersistOptions options); /** * 将一个实体对象更新到数据库中。 */ bool Persist(T entity); bool Persist(T t, string[] properties); bool Persist(T t, PersistOptions options); /** * 删除一个对象。 */ bool Delete(T entity); bool Delete(T t, PersistOptions options); int DeleteChild(T t, Type subType); /*如果实体在数据库中不存在则插入,反之修改 */ bool Attach(T entity);
/** * 根据主键查找某个对象,参数是要查找的对象,这个对象必须携带主键的信息,否则抛出异常 */ bool FindByPrimaryKey(T entity);// where T : new(); bool FindByPrimaryKey(T entity, PersistOptions options);// where T : new(); /** * 获取引用的对象 */ bool GetReference(T entity); bool GetReference(T entity, Type refType); object GetReference<K>(T entity); /** * 获取某个实体对象的子对象。 */ bool GetChildren(T entity); bool GetChild(T entity, Type childType); object GetChild<K>(T entity); /*查询对象*/ List<T> QueryObjects(); List<T> QueryObjects(string filter, QueryParameterCollection qpc); List<T> QueryObjectsWithSql(string cmdText, QueryParameterCollection qpc);
/*保留ADO.Net接口*/ DataSet ExecuteDataSet(string cmdText, QueryParameterCollection qpc); void ExecuteDataSet(DataSet ds,string table,string cmdText, QueryParameterCollection qpc); int ExecuteNonQuery(string cmdText, QueryParameterCollection qpc); IDataReader ExecuteReader(string cmdText, QueryParameterCollection qpc); object ExecuteScaler(string cmdText, QueryParameterCollection qpc); } |
Websharp.ORM 2.0使用方法
以保存一个产品为例说明使用Websharp.ORM 2.0过程,首先声明一个产品类
[Serializable()] [TableMap("Products", "ID")] public class Product {
private Int private String m_Name; private String m_Price; public Product(){}
// ID [AutoIncrease()] public Int32 ID { get { return this.m_ID; } set { this.m_ID = value; } }
// Name public String Name { get { return this.m_Name; } set { this.m_Name = value; } }
// Price public String Price { get { return this.m_Price; } set { this.m_Price = value; } } } |
使用Websharp.ORM 2.0保存一个新的Product
PersistenceManager<Product> pm = PersistenceManagerFactory.Instance().Create<Product>(); Product p = new Product(); p.Name = "数码相机"; p.Price = 3000; pm.PersistNew(p); |
ORM为Product类生成一个强类型的持久化管理器,插入部分的方法如下:
public class MSSQLServerProductPersistenceManager : AbstractPersistenceManager<Product> { public override bool PersistNew(Hengjie.Entities.Product product) { string cmdText = null; cmdText = "INSERT INTO Products(Name,Price) VALUES(@Name,@Price) SELECT @ID=@@IDENTITY"; Websharp.Data.QueryParameterCollection qpc = new Websharp.Data.QueryParameterCollection(); qpc.Add("@Name", product.Name, DbType.String); qpc.Add("@Price", product.Price, DbType.String); qpc.Add("@ID", product.ID, DbType.Int32); qpc[2].Direction = ParameterDirection.Output; bool flag = this.Execute(CommandType.Text, cmdText, qpc); if (flag) { product.ID = int.Parse(qpc[2].Value.ToString()); if (!IgnoreCache) { CacheProxy.CacheEntity(this.BuildKey(product), product); } } return flag; } } |
下面是Websharp.ORM 2.0的静态类图:
用户需要一个 OrderPersistenceManager 的时候,通过PersistenceManagerFactory.Create<T>()方法创建,在该方法中PersistenceManagerFactory首先判断持久化管理器是否被缓存,如果没有的话,调用Websharp.ORM的编译器(PersistenceManagerGenerator<T>的子类),在内存中编译所有的实体对应的持久化管理器,然后把这些持久化管理器类型缓存起来,最终从缓存中取得这个持久化管理器的类型,通过反射创建一个持久化管理器实例。具体过程如下:
如下顺序图所示
查询
大部分的ORM会有一个Query类来处理查询功能,本人认为在返回关联表的时候(SELECT 语句包含多个表的列)才有必要使用Query对象,Websharp.ORM 2.0不能处理返回关联表的情况,所以没有必要单独提出一个Query类,所有的查询都在持久化管理器的QueryObject()方法中完成。
关系
实体之间的关系主要有一对多、一对一、多对多。
关系型数据库中表的关系,主要体现在表的主、外键上。根据主外键把关联的两个表称作主表和从表,主表包含主键,从表包含外键。
主外键表示关系非常精炼,在理解上,这里有三个需要强调的地方(表述未必正确):
1, 外键的类型和主键数据类型必须一致
2, 实际关系只体现在外键上。具体说就是主表不知道那些从表设置外键引用了自己(主表不知道自己的儿子是谁,也不知道自己又没有儿子);但是从表知道自己的主表是谁(通过外键,知道自己的老爸是谁)。
3,多对多的关系必须通过两个以上的外键体现。实际上数据库中不存在多对多关系。
OO中主要用静态类之间的关系,UML把类之间的关系分为三种类型:
1, 依赖(Dependency)
2, 泛化(Gerneralization)
3, 关联(Association)
对于ORM中讨论的实体关系主要体现在关联上(继承也涉及到
配置
Websharp.ORM 2.0的配置放在app.exe.config文件中,对应Websharp.ORM配置节
<Websharp.ORM> <DatabaseProperties> <DatabaseProperty name="default"> <DataBaseType>MSSQLServer</DataBaseType> <ConnectionString>server=(local);database=Hengjie;uid=sa;pwd=foolnix</ConnectionString> </DatabaseProperty> <DatabaseProperty name="access"> <DataBaseType>OleDBSupported</DataBaseType> <ConnectionString>Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Wujing\MainForm\bin\Debug\Wujing.mdb;User ID=Admin;Password=;</ConnectionString> </DatabaseProperty> <DatabaseProperty name="oracle"> <DataBaseType>Oracle</DataBaseType> <ConnectionString>Data Source=Wujing;user id=foolnet;pwd=foolnet</ConnectionString> </DatabaseProperty> </DatabaseProperties> <!-- PersistenceManagerType { Cached, StoredAssemble }--> <PersistenceManagerType NameSpace="Websharp.ORM.PersistenceManagerImp" Assembly="\Websharp.Tool.dll">Cached</PersistenceManagerType> <!-- SqlCommandType { Text, StoredProcedurey }--> <SqlCommandType>Text</SqlCommandType> <OutPutSourceCode SourceCodePath="e:\temp">true</OutPutSourceCode> </Websharp.ORM> |