日期:2014-05-17  浏览次数:21016 次

ORM组件XCode(十八般武艺)
之前,XCode总是若隐若现,耐性好的同学想知道它还有啥特点,沉不住气的则认为不过是CURD耳!

 

XCode开发模式是灵魂,XCode组件通过具体实现对其支持!

 

XCode的特点如下:

0、基本的CURD功能

实在想不出来不支持CURD的ORM算不算ORM;也实在想不出来仅有CURD的ORM算不算ORM。因而,这是0号功能!

XCode的CURD通过反射实体类生成查询和操作SQL实现,数据库结构信息通过特性附在实体类上。之所以选择SQL而不是DbCommand,因为XCode的实体层和数据访问层是分开的,目前是为了实现一级缓存,将来会在这里实现分布式数据访问。

1、完美支持ObjectDataSource

XCode实现充血模型(胀血模型)的实体类,提供ObjectDataSource需要的所有方法和参数,特别支持分页和排序功能!

详见《与ObjectDataSource共舞》

2、全面分页支持

只有从小处开始培养分页的思想,任何查询都指定所需获取数据范围,才能保证系统数据变大时系统不会拓机。

XCode的分页以任意查询语句为基础,支持统计等非常复杂的查询分页。并且会根据当前数据库类型以及版本选择最佳分页方案。

详见《撬动千万级数据》

3、实体集合支持

实体集合EntityList<TEntity>继承自List<TEntity>,提供了实体的批量操作。实际上还是通过遍历集合逐个进行实体操作,因为充血模型的实体类可能是通过重载修改CURD的行为,所以不能使用一个SQL语句操作一批实体,XCode不会做这种可能会影响使用的小把戏。

实体集合还提供了一些方便查询和排序的简便方法,实体缓存中将会大量使用。

4、万能的一级缓存

一级缓存由数据访问层实现,以查询SQL为键,返回的数据集为值,查询的表名数组为依赖项,进行缓存。执行SQL时同样需要指定影响的表名数组,从而清空所有影响到的缓存。

缓存生命周期分为请求级、定期和永久三种。如果只有当前应用系统使用该数据库,并且服务器内存足够大,可以开启永久缓存,在数据没有更新时,基本缓存在内存中,适用于网站;一般设定一个缓存过期期限,定期清理缓存,适用于内存不是很足,或者允许数据更新有一定延后的分布式系统;如果上面两种均不合适,而又需要提高系统响应速度时,可以采用请求级缓存,在该次页面请求生命周期内对数据进行缓存,特别适用于在不同地方进行相同查询的场合(有时候是程序员功力不够写错的)。

XCode的开发模式建议使用尽可能简单的单表查询,实际大部分查询都是简单SQL语句,缓存命中率很高!

5、漂亮的实体缓存

实体缓存又成为二级缓存。尽管有了一级缓存,但它只是缓存了数据集而已,使用的时候还是要加载数据集成为实体集合。

实体缓存通过指定一个查询实体集合的方法,一般是查询本表所有实体的FindAll()方法,把查询返回的实体集合缓存起来(默认缓存一分钟),供上层代码使用。取数据的过程完全是隐式进行,实体缓存通过提供一个静态实体集合供上层代码查询,实体集合属性内部进行查询数据和缓存过期检查等操作。查询方法通过委托传递,还有参数可以指定是否异步获取缓存数据。

总之,使用实体缓存就是使用一个静态的实体集合属性(大多数时候使用默认配置,所以不需要配置),进行查询排序等操作,无需关心缓存的具体实现。当然,对实体进行修改操作时将会清空缓存,保证数据的新鲜性。

单表数据量不大(建议1000以下,不超过10000),并且极少改动的数据表使用实体缓存。比如权限、角色、菜单、系统参数等使用非常频繁的数据。实体缓存的命中率可以高达99.98%

6、飘逸的单对象缓存

单对象缓存又层三级缓存,因为它一般构建于二级缓存之上。对于数据量大(大概几万到几十万),并且查询又非常频繁的数据表,任意两行数据之间关系不大时,可以酌情使用单对象缓存。比如会员表,一般会根据账号进行查找,并且很频繁,此时可以以账号为键,会员对象为值,对数据进行缓存。设置与实体缓存类似。取数据时先去缓存中找,有则直接返回,没有则调用预设的方法进行查询,并且缓存起来。

单对象缓存里面的实体对象,修改数据时,如非必要,不要手工调用更新方法,单对象缓存有自动保存的功能。该特性适用于更新非常频繁的场合,比如在线用户表,可以让多次更新积累在一起,然后最后自动更新一下。

7、出色的性能

XCode不支持多表查询,一般的多表关联查询都可以拆分成为1+X的多次单表查询。比如学生和班级的关联查询,可以先查10个学生信息,再分别查他们的班级信息,就成了1+10=11次单表查询。每一次单表查询肯定会比多表关联查询要快,但是11次单表查询很多时候都会比一次多表关联查询慢。

回过头来看看上面的缓存,如果这10个学生是同班,那么在一级缓存的作用下,实际查询数据库将会是1+1=2,后面9次班级查询被一级缓存拦截了。在高并发的系统中,后面这个1就趋向于0了,因为缓存是全局共享的。

再来看看实体缓存,一个学校的班级不会很多,符合条件使用实体缓存。也就是一次性读取所有班级信息,缓存到实体集合中。即使在最糟糕的情况下,10个学生都处于不同班级,实体缓存也是百分百命中,实际查询仅仅是对学生表的单表查询,此时肯定比多表关联查询快。

在学生资料界面等地方,学生表查询是非常频繁的。显然,这是一个非常适用单实体缓存的场合。学生附属属性(关联表)等信息,可以通过扩展属性“挂”在学生实体对象上,“享受”到缓存的待遇。

数据库层面也有一个缓存,可以算是0级缓存吧。我们所有的查询都是单表查询,对数据库而言非常简单,同时,因为简单的SQL,数据库缓存命中率极高,并且非常便于建立索引进行优化。

基于分页和缓存,XCode提供了一套高性能的解决方案,这种方案远胜于传统的多表关联查询,并且是系统并发越高,这种优势越明显。

8、脏数据支持

在更新数据的时候,往往业务需求是只更新我们修改过的数据。比如会员资料修改表单,可以设置会员信息等资料,但是不能修改最后登录时间。这个时候,我们就需要知道哪个属性的数据被我们修改过!

XCode的实体类中,每个数据属性的set方法,都会先调用OnPropertyChange方法,其实就是为了设置该字段的脏属性,说明这个字段的数据曾经被修改过。生成Update语句的时候,只修改带有脏属性的字段。

实体类中,除了直接修改属性外,还可以通过索引器进行修改,两种的区别就在于通过索引器修改属性时,不影响脏数据设置。实际上,加载数据行到实体类中,使用的正是索引器,所以刚加载完成数据的情况下脏数据是空的。

9、多数据库支持

(MSSQL2000、MSSQL2005/2008、Oracle、MySQL、Access、SQLite)

与大多数ORM一样,XCode通过接口的方式支持多种数据库。在XCode中,为每一个数据库实现了一个数据库操作类,继承自数据库接口。数据访问层DAL根据数据库连接的配置识别是哪一种数据库,然后创建该数据库操作类的实例,并通过操作接口来操作数据库。

数据库操作类以Access数据库为蓝本,设计了一个基类,其它数据库操作类仅需要继承该类,重载功能点不一致的属性和方法,大大减小了操作类的大小。

数据库操作接口包含的功能有:查询、执行、分页、事务、获取架构、DDL操作、数据库版本等。实际上,各个数据库的差异点都可以设计在操作接口中,而上层代码根本不需要改动。

很多ORM都为各个数据库的差异大而苦恼,XCode开发模式则不然。我们的原则是一切从简,只使用SQL,不适用DbCommand和存储过程。而所使用的SQL,基本上也是标准SQL,不会使用数据库特性,并且都是单表操作。当然,这种方法也不是万能的,不得已的时候,可以在业务层判断当前数据库类型,根据不同数据库编写不同的SQL,但自XCode使用以来,还没需要这样做过。

10、获取数据库架构

(DAL.Tables)

在XCode中,数据库架构主要包含XTable和XField类,顾名思义,它们代表着表和字段信息。数据访问层DAL中有个成员属性Tables可以取得该数据库连接的所有表信息,也就是一个XTable集合。同理,每一个XTable中,都会有一个XField集合。

这样设计,简单明了