简单,再简单一点
2007年9月2日 #
2007年8月28日 #
2007年7月29日 #
2007年7月26日 #
2007年7月3日 #
2005年1月26日 #
令人心动的对象化查询
对象化查询是O/R Mapping中非常让人心动的一个特性,当所有的表对我们来说不复存在后,我们可以对实体进行CRUD,可是当我们需要返回多个实体的结果集时,我们怎么办呢?Dataset做为载体是必然的,可是我们查询过滤数据的方式是怎样的?sql?No!我们就是在尽量避免直接使用sql,因为sql不便于重构,可移植性太差,拼凑出来的sql语句对测试覆盖率也有影响。所以大家都期待着可以用对象化查询来根除将Entity再当做Table来处理的尴尬。
.Net下的O/R Mapper对对象化查询的支持
目前为止,.Net下的O/R Mapper对对象化查询的支持绝大部分都是非常有限的:NHibernate用的是HQL语句,其本质可以说是一种把Entity看做Table的改装后的sql语句,DataObjects.Net和NHibernate差不多,也有一套自己的类sql对象化查询语法,其它很多O/R Mapper使用的是简单的属性过滤查询,而说到对对象化查询支持得最强大的,要算ECOⅡ和ObjectSpaces,前者使用的是OCL语法,后者是OPath。
OCL起源
OCL是OMG组织制订的UML语言规范的一部分,它的初衷是用来对UML模型视图中的Object添加约束描述的,并不是一种编程语言,而OCL应用到O/R Mapping中就俨然成了一种对对象进行查询的语言(OQL)。
OPath起源
OPath是MS提出的在WinFS中准备大量使用的一种对象查询语言,从它的命名上可以看出和XPath的相似之处,MS的口号一直是把复杂的东西变简单,把简单的东西变成自动化。所以这个OPath确实不可小觑。
简单的例子
先我们从一个简单的例子入手,假设有如下的实体关系(画图太麻烦,还浪费空间):
Department (code name employees)
Employee(code name department salary)
查询
OCL
OPath
包含薪水大于3000的员工所有部门
Department.allInstances->select(self.employees.includes(salary > 3000))
Exists(employees[saleary > 3000])
得到部门001的所有员工
Employee.allInstances->select(self.department.code = ‘001’)
Department.code = ‘001’
所有员工薪水都大于3000的部门
Department.allInstances->select(self.employees.forAll(salary > 3000))
不支持
员工最高薪水大于3000的部门
功能比较
从这个列表中可以看到OPath的语法要简洁得多,但OCL的集合操纵能力却是OPath无法比拟的,下面是OCL的一些集合函数:
名称
功能
Collect
得到一个属性的集合
Exists
如果存在符合条件的元素为真
forAll
只有所有集合元素符合条件时才为真
Includes
是否包含某个对象
Select
获取符合条件的一个子集
Reject
减去符合条件的一个子集
IsEmpty
集合为空
NotEmpty
集合不为空
此外,ECOⅡ对集合操作还有增强,如提供了sum、avg、max、min的聚集操作函数,同时它还支持多种非聚集函数,如concat、length、toupper、tolower等。
其它的一些特性:
特性
NULL判断
支持
Implies(表示如果左边为真,那么右边也要为真)
A implies B可以转化为(A and B) or (Not A)
类型判断和转换
无
Distinct
支持(通过Set集合返回)
不支持(因为只是对象获取和属性过滤,没有集合操作,所以不涉及到子集合,而外部对象集合一般有主键约束)
集合内元素获取
Order By
不支持,但可以通过调用查询对象的方法达到目的
假如增强OPath的话,大概会是什么样子?
如果要支持集合操作的话,那么现有的OPath语法的简单性肯定要大打折扣,比如说类似于OCL中的forAll,OPath如果要实现的话,可能的方法就是:
(Employees[salary > 3000].Count = Employees.Count)
性能比较
因为现在ObjectSpaces中的OPath语句最后都将转为SQL执行,所以它的性能是非常好的(时这也正是它功能无法强大的弱点),而OCL的语法只有部分才能转化为SQL,因此在ECOⅡ中,还有部门的OCL操作必须针对内存对象来进行,这样也就使得必须装载大量的实体对象到内存中。
你将选择哪种查询?
就我个人而言,我倾向于增强型的OPath。毕竟更加复杂的查询我们可以通过view和store procedure来完成,就算OCL再强大,也无法涵盖我们需要的查询功能,其内存对象操纵的性能问题不能不让我们谨慎使用,而ECOⅡ可以算是一个MDA的框架,这也让我对MDA的将来感到一些担忧,除非ANSI SQL进化到了可以更加强大的地步,各数据库本身就可以完成OCL所需要的查询功能,但这个时候,也许SQL的表达更加直观,那么,我们又为什么需要OCL来查询呢。当然,OCL在UML中的作用我们并不否定,只是直接从模型生成代码的应用似乎只能在不太复杂的场合才生效。
(注:因为ECOⅡ的资料很少,很多是参考它的前身bold for delphi的语法,而OPath的资料也少,很多都是无关痛痒的简单操作,这让人弄不明白到底是它的语法就这么简单还是另有其它的增强。)
2005年1月14日 #
先建立一个简单的表结构
这样两句查询:
2005年1月12日 #
数据库定义到char类型的字段时,不知道大家是否会犹豫一下,到底选char、nchar、varchar、nvarchar、text、ntext中哪一种呢?结果很可能是两种,一种是节俭人士的选择:最好是用定长的,感觉比变长能省些空间,而且处理起来会快些,无法定长只好选用定长,并且将长度设置尽可能地小;另一种是则是觉得无所谓,尽量用可变类型的,长度尽量放大些。 鉴于现在硬件像萝卜一样便宜的大好形势,纠缠这样的小问题实在是没多大意义,不过如果不弄清它,总觉得对不起劳累过度的CPU和硬盘。 下面开始了(以下说明只针对SqlServer有效): 1、当使用非unicode时慎用以下这种查询: select f from t where f = N'xx'
原因:无法利用到索引,因为数据库会将f先转换到unicode再和N'xx'比较 2、char 和相同长度的varchar处理速度差不多(后面还有说明) 3、varchar的长度不会影响处理速度!!!(看后面解释) 4、索引中列总长度最多支持总为900字节,所以长度大于900的varchar、char和大于450的nvarchar,nchar将无法创建索引 5、text、ntext上是无法创建索引的 6、O/R Mapping中对应实体的属性类型一般是以string居多,用char[]的非常少,所以如果按mapping的合理性来说,可变长度的类型更加吻合 7、一般基础资料表中的name在实际查询中基本上全部是使用like '%xx%'这种方式,而这种方式是无法利用索引的,所以如果对于此种字段,索引建了也白建 8、其它一些像remark的字段则是根本不需要查询的,所以不需要索引 9、varchar的存放和string是一样原理的,即length {block}这种方式,所以varchar的长度和它实际占用空间是无关的 10、对于固定长度的字段,是需要额外空间来存放NULL标识的,所以如果一个char字段中出现非常多的NULL,那么很不幸,你的占用空间比没有NULL的大(但这个大并不是大太多,因为NULL标识是用bit存放的,可是如果你一行中只有你一个NULL需要标识,那么你就白白浪费1byte空间了,罪过罪过!),这时候,你可以使用特殊标识来存放,如:'NV' 11、同上,所以对于这种NULL查询,索引是无法生效的,假如你使用了NULL标识替代的话,那么恭喜你,你可以利用到索引了 12、char和varchar的比较成本是一样的,现在关键就看它们的索引查找的成本了,因为查找策略都一样,因此应该比较谁占用空间小。在存放相同数量的字符情况下,如果数量小,那么char占用长度是小于varchar的,但如果数量稍大,则varchar完全可能小于char,而且要看实际填充数值的充实度,比如说varchar(3)和char(3),那么理论上应该是char快了,但如果是char(10)和varchar(10),充实度只有30%的情况下,理论上就应该是varchar快了。因为varchar需要额外空间存放块长度,所以只要length(1-fillfactor)大于这个存放空间(好像是2字节),那么它就会比相同长度的char快了。 13、nvarchar比varchar要慢上一些,而且对于非unicode字符它会占用双倍的空间,那么这么一种类型推出来是为什么呢?对,就是为了国际化,对于unicode类型的数据,排序规则对它们是不起作用的,而非unicode字符在处理不同语言的数据时,必须指定排序规则才能正常工作,所以n类型就这么一点好处。 总结陈词: 1、如果数据量非常大,又能100%确定长度且保存只是ansi字符,那么char 2、能确定长度又不一定是ansi字符或者,那么用nchar; 3、不确定长度,要查询且希望利用索引的话,用nvarchar类型吧,将它们设到400; 4、不查询的话没什么好说的,用nvarchar(4000) 5、性格豪爽的可以只用3和4,偶尔用用1,毕竟这是一种额外说明,等于告诉别人说,我一定需要长度为X位的数据 这样一来,生活是不是变成美好多了? 如果还有没明白的,那么还是省点钱去买萝卜吧。
2004年12月26日 #
功夫看了枪版(为自己汗颜一下,实在是等不及了,而电影院多少年没有进去了已经),然后开始在网上找相关评论,就好像是自己的东西一样,期待着人们有好的反应,结果大家也知道,毁誉参半,或者说批评还要多一些,说星爷放弃了擅长的无厘头,玩起来了特效,失去了自我。 Persistore是什么? 一个我正在开发的for .Net的O/R Mapping框架,大家可能奇怪了,现在成熟半成熟的.Net下的O/RM都已经多得让人眼花缭乱了,为什么还要自己开发一个?! 难道我这么喜欢无意义的重复开发,并且想依靠这个东西来证明自己的能力? 再回到功夫上来,可以说,我一向欣赏的是香港电影讲故事的能力和星爷无厘头的表演,对于国产大片(以英雄、埋伏为代表)和好莱坞大片却并不感冒,因为我需要的不是视觉的冲击而是看电影时的放松和快乐,哈哈一笑,仅此而已,然而功夫在这方面带给我的这些东西非常少,可以说让我很“失望”,但是同时我们应该知道,功夫是星爷的再一次尝试,为了圆一个儿时的梦,用三年时间,换来一部风格迥异的电影,当然他自然也会从票房着想,为了国际化,更多地简化语言,强调动作。 我不喜欢功夫,但是我欣赏星爷的这种尝试和努力,同时Persistore也是我自己的尝试和努力,因为我一直以来就想开发我所希望的O/RM,.Net出现已经4年了,而我现在才正式开始进入.Net开发,然而我这个想法一直以来就没有改变过。 那么Persistore有哪些特性?它凭什么可以让我不选择其它的成熟解决方案?就像黑客帝国、指环王中的特效已经这么好了,为什么功夫还要玩特效? 1、纯实体类 这是我对自己的O/RM最重要的一个要求,纯实体类不仅轻,而且可以便于移植(有人说了,谁会去移植?!),可能是我太偏执的原因吧。 2、自动映射 Persistore不需要自定义属性和XML文件的支持便可实现映射。 我们已经有了数据库模型了,我们已经有了实体类了,为什么还需要再去维护映射?! 3、关系维护 OnetoOne、OnetoMany、ManytoMany、SelfAssociate支持,正如yyanghhong所说,不支持关系的O/RM还不如直接用typed-dataset. 4、自动创建数据库结构(需要自定义属性支持) Class->Database已经成为潮流了,本来不准备支持的,但同事AdamBear说得很对,在早期的迭代式开发中,难以建立完整的数据库模型,所以实体类是优先考虑的。和其它框架不一样的是,Persistore不需要创建额外的字典信息表,同时可以在主键不改变的情况下动态添加、删除、更新字段的定义。 5、lazy-loading 在支持关系维护后,没有迟加载的话,那么对于性能就是致命的下降。Persistore可以迟加载对象属性、实体集合,并且对于迟加载的实体集合只使用一次查询完成。 6、实体继承 Persistore完全支持实体继承,可以同时支持JoinTable和UnionTable两种持久化方案,即分表保存和单表保存。 7、对象化查询 现有的O/RM中只有极少数的(如ECO)支持真正的对象化查询(即UML2.0中的OCL),hibernate的HQL实际上还是sql的变种,Dataobjects.Net里的也还是sql,不过比sql还难理解,Persistore的OCL支持同样也是有限的(扩充了函数,但减弱了集合操作,因为OCL最终将解析成SQL),它的形式如下:
Department.Employees->Sum(salary) > 100
重要的是对关系的查询,而不仅仅是属性的过滤。 8、跨数据库平台和SqlBroker 从一开始,Persistore的设计就是要支持多数据库平台的,各大数据库厂商对标准SQL的支持越来越好,同时标准SQL本身的功能也在增强,这使得平台无关的SQL语句已经可以完成绝大多数功能。通过Persistore执行的sql只能是被其支持的标准SQL。 9、View、Store Procedure支持 这是一个数据库应用程序必不可少的,同时弥补上面SqlBroker的不足。 10、其它特性 Criteria查询构造、NULL处理、事务支持(transaction和savepoint)、不完全实体对象查询、多种"脏"标志获取方法(default value、snapshot value、dynamic proxy)、延迟更新和批量更新。。。
还需要多久? 目前差的是OCL和ANSISQL的解析(语法分析已经完成)、Store Procedure的封装以及最重要的实体类自动生成工具(CodeDG已经够灵活了,但它还不能提供Persistore最需要的外键约束信息)
如何使用? 一个简单的例子:
最后说点什么 写代码真的好累,有时候真的不想写了,但想想星爷,想想功夫,即使做出一个被人批评的东西,但至少事后我可以无悔地说:我做过了!
2004年12月6日 #
今天,特意去看了下Comega,其它的特性暂时都略过,只是看它的语言对sql的直接集成,只能说是震惊!!! 先不说它的sql语法特性支持是否强大,因为这些是可以慢慢增强的,我最欣赏的是它通过引用根本数据库结构创建的Northwind.dll,可以对实体类直接进行编译期检查(实际上,编辑时就已经可以动态检查了)。 要知道,这对我们的数据库开发意味着什么! 重构、消除硬编码、可读性、开发效率提升、数据库平台无关。。。O/R Mapper再优雅,也必须要进行映射,只要稍复杂的系统,要屏蔽开发人员对数据库的了解都是不可能的,而O/R Mapper分离business layer和data access layer的功能对于Comega来说完全没有冲突,DAL层和BL照样可以存在,而生活却一下子变得美好了很多。 可以说,从一个数据库开发人员的角度来说,Comega的这种特性也正是我所需要的,好像Andres Hejlsberg在讲C#3.0时也提到了类似的特性,不错,我们要的就是这种东西。
对于很多人来说,存储过程的不可替代性并不是在于它的性能(编译优化对性能能提升多少?),而在于它的编译检查,当你一个表改变名称的时候,你很容易知道哪个存储过程中引用了它(当然前提是你不能写动态SQL),Comega的sql支持让我们在不使用SP的情况保存了这种优势。 当然就目前来说,只是简单的SQL支持远不能取代SP,但Comega的出现让我们看到了光明的所在。O/R Mapper,什么时候我能彻底抛弃呢?在关系的维护和编译期检查数据库对象能力之中,我宁愿不要关系维护,当然Comega并不和RelationShip支持背道而驰,当这种代码: Customer c = (Customer)Session.Load(typeof(Customer), "001"); 被
Customer c = select Customer from DB.Customers where code = "001"; 取代时,我看到的是一种简洁和痛快。 而且就目前的OCL而言,再怎么支持都是在字符串中写表达式或者通过函数来构造表达式,于是在后者,有的方案便通过给Entity class 加入描述属性名称的public const 字段用来构建expression,但这些,都不是我所希望的。再怎么构造查询,都远不如sql的可读性强,因为sql是描述和操纵RDBMS的最好语言。 我一直是悲观主义者,尽管我目前在写自己的O/R Mapper框架,但是我依然不看好它。而要彻底地改变目前O/R Mapper的可用性的话,是O/R Mapper中的突变,抑或是类似Comega的直接CLR支持取而代之,还是对象化数据库一统天下,将前两者容为一体?
Powered by: 博客园 Copyright © progame