深入ADO

Active Data Objects结合了OLE DB的普遍性质—那就是在诸如RDO和DAO模型中可以找到的易于使用的

特性.ADO包含了所有可以被OLE DB标准接口描述的数据类型.换而言之,ADO是可扩充的,不需要对你的

部件做任何工作.

数据的访问和操纵是任何实际的应用程序的一个固有部分.对于数据来说,无论它是否是关系型的,无论

它是否存在一个DBMS,也无论它的存储平台是什么,数据就是数据—一个文件没有必要一定是给定的二进

制格式.

开发者群体需要具有简单接口的现代的开发工具以快速访问数据.微软对此问题的回答是Universal

Data Access(UDA通用数据访问)体系结构,对此,Stephen Rauch的文章 “Manage Data from Myriad

Sources with the Universal Data Access Interfaces”中有详细的阐述(MSJ,1997年9月).简单的

说,UDA是一种将OLE DB应用于实际的理论.所有的都被指向一个数据源—一个电子表格,一条电子邮件

消息,或一份AS/400文档—由OLE DB接口过滤并以一种通用的格式表示,这样应用程序能总是以同样的

方式对数据进行访问.位于OLE DB上的并处理来自应用程序的调用的中间层被称作Active Data Objects

(ADO).它是编写针对带有OLE DB提供者的任何类型的数据源的推荐标准.



图 1 OLE DB

让我们仔细看看Visual Studio 6.0所带来的ADO 2.0的新特性.我将向你展示如何用Visual Studio 6.0

套件来编写ADO.我将集中于Java语言的Windows Foundation Classes(WFC)的和Visual Basic的内置的ADO

支持,对于Visual C++和InterDev将略微提一提.

 

ADO概述

ADO是一个对象模型,它结合了OLE DB易于使用的特性以及在诸如Remote Data Objects(RDO)和Data

Access Objects(DAO)的模型中容易找到的通用特性.ADO是一个可以通过IDispatch和vtable函数访问

的COM自动化服务器.最重要的是:ADO包含了所有可以被OLE DB标准接口描述的数据类型.换而言之,

ADO对象模型具有可扩展性,它不需要你对自己的部件做任何工作.通过通常的ADO编程接口,你可以可

视化地处理所有的事,即使那些记录集的信息的格式是你从来没有想到过会见到的.

ADO在其实际运行中得到了很高的评价,内存覆盖,线程安全,分布式事务支持,基于Web的远程数据访问.

作为Microsoft UDA策略的一部分,ADO试图成为基于跨平台的,数据源异构的数据访问的标准模型.随着

时间的流逝,它将取代其他模型.ADO集中了RDO和DAO的所有最好的特性,并将它们重新组织在一个同样

可以提供对事件的充分支持的略微有点不同的对象模型中.如果你想深入的钻研一下Microsoft的数据

访问技术之间的不同,你可以看看 “Data Access Technologies”,这是由Robert Green写的MSDN的

关于技术性方面的文章.

将你当前的基于RDO的系统移植为ADO的不需要对系统的完全的重构,但它也不是件微不足道的事情.移

植系统的难度和复杂性比RDO和ADO的差异大.移植为ADO是很有价值的,但是我建议如果不是绝对必要

的话请你不要这样做.

 

从企业的观点来看ADO

UDA给Windows DNA(Distributed Internet Applications分布式互连网应用程序)体系结构以一个数据

访问和存取的机制.为进一步了解UDA,你可以看 “Say UDA for All Your Data Acess Needs,”该文

是由Aaron Skonnard写的(见Microsoft Interactive Developer,1998年4月).

即使是跨了多个硬件平台,任何一个分布式企业系统都有多个数据源,包括Microsoft Access数据库,

电子表格和SQL Server表.最近,我参加了一个异构的图象数据库的研究,其内容包括:SQL Server表

(包括图象及其描述),通过与名字相关的ASCII码和Word文档对图象进行分类索引,所有者文件中既

有图象又有文本.当你需要对付这样的工程时,你会开始认识到统一的方法和对象公共集的重要性了.

我曾经见过在相关的工程中使用不同的访问数据方法的开发团队.这种情况是经常发生的,这是因为

其中一个团队相当早的提前对子项目进行了开发,或者由于它们继承了原来的一些代码,或出于更简

单的原因,即这些工程只是部分相关,它们之间的公共部分是底层的数据,或者可能是买主.所以这样

就出现了一个团队用RDO进行数据操作,而另外一个团队用ADO.这样的事是常常发生的:即类似的代码

被重复开发了两次(或更多),一次是RDO的,一次是ADO的.在工程间的公共层就成了物理数据存储了(见图2).



图2 数据访问技术

在ADO之前的RDO是一种增加DAO的客户/服务器能力,以提高其性能和可扩充性的当然的方法.根本上说来,

RDO是一种位于ODBC API的上层的简便的封装.它揭示了了DAO数据对象模型中的许多东西,但它缺乏进行

数据访问的Jet引擎.虽然这将予ADO更快的速度,但它没法利用该引擎的许多特性,而且它只能访问关系

型的数据库.

ADO 2.0的思想就在于:为不同的应用程序访问相同的数据源创建一个更高层的公用层.如果你比较图1

和图2,你应该会明白我的意思.尽管存在数据结构和组织间的物理位置的不同,编程的接口应该是一样

的.为了找出RDO和ADO之间的更多的不同,我建议你阅读Bill Vaughn写的非常好的一篇文章 “Exploring

ActiveX Data Objects from an RDO Point of View,"你能在MSDN的技术性文章部分中找到它.ADO的出

现并不意味着RDO的结束.实际上,微软承诺在可以预见的将来继续支持RDO.

 

ADO 2.0有什么新特点?

对于ADO1.5以前包括1.5的版本来说,从功能的角度来看RDO和ADO不是完全相等的.等同就意味着你可

以通过这两种方法解决同样的问题;它不是指存在重命名的或者优化的功能相同的对象.因此,移植到

ADO不是一个简单的事情.从另一方面来说,一旦你熟练掌握了RDO或DAO技术的话,学习ADO是件相当容

易的事情.

ADO 2.0的新特性包括事件处理,记录集的延续,分层目录结构指针和数据成形,分布式事务处理,多维

数据,远程数据服务(RDS),以及对C++和Java的支持的增强.在钻研一些Visual Basic代码的时候将会

见到所有的这些特性.当使用Visual J++时,我将举例说明新的Windows Foundation Classes(WFC)是

如何支持ADO的.ADO的最让人激动的是在Visual Studio 6.0中的任何开发工具中你都可以找到对它的

充分的支持.

 

ADO快速教程

ADO对象模型是由相对数量较少的对象组成.不象RDO对象模型,每个主要的ADO对象都能被个别的被创建.

这就意味着,举例说吧,你不需要在创建有效的记录集对象前创建一个连接.

Dim cn As New ADODB.Connection

Dim rs As New ADODB.Recordset

cn.Open "Biblio"

rs.Open "select * from titles where title like ‘%h‘", cn

ADO 2.0对象是Connection,Command,Parameter,Recordset,Field,Error和Property.ADO对象模型也包

括四个类集:它们分别是一套相关的Error,Parameter,Property和Field对象.让我们来看看每个对象的

主要特性.

Connection对象提供连接,该连接连接的程序中存有它正在访问的数据源.属性允许你定义:连接串,命令

执行以及连接启动的间隔时间,数据提供者,(无论临时表位置是应该在客户端还是在服务器端),和对于

数据的访问权限.方法有关于如下功能的:执行命令,打开和关闭一个连接和管理事务.

在许多地方你可以指定你想使用的提供者:通过Provider属性,在连接字符串中,或者甚至通过Open方法.

选择你最喜欢的实现方式,但要保证你没有为同样的连接指定多个的提供者.缺省的提供者(当

你没有指定你自己的提供者的情况下)是MSDASQL,针对ODBC的Microsoft OLE DB提供者.

Command对象定义了一个SQL声明,一个存储过程,或任何其他的你可能想让提供者执行的关于数据的操

作.Command的属性有:当前连接,最大允许执行时间和参数集.一个参数就是你将它作为参数传递给命令

的值.在这些属性中用以区分一个参数的属性有:方向(输入,输出,或者两者都有),类型当然还有它们的值.

记录集对象也许是最典型的ADO对象,然而它是最复杂的当中的一个.它表示命令执行的结果,并且它大

多是以数据库的记录集的形式给出.一个记录集是由以行和域的形式表示的信息组成.它不一定非要映射

成关系型数据库的记录.实际上,如同我早些时候解释的那样,ADO是基于OLE DB并且能被用来可视化地访

问任何数据源中的数据,这数据源包括非关系型的数据库.记录集提供了缓冲能力,它接受数据的变化,并

且将这些变化以批处理的方式传给服务器.你可以浏览并对记录集的内容分类,如同列举和提取行.你也

能通过如:删除,添加和刷新操作等任何方式修改数据.记录集存在跟连接的严格的关系,但是这不意味着

你总是需要一个打开的连接来获得一个记录集.你也可以按如下方式进行:

Dim RS As New ADODB.Recordset

sql = "select * from authors"

RS.Open sql, "Pubs"

既然Connection实际上是数据和命令传送的通道,所以该对象仍然被创建了,除了一个名字叫做

Recordset.ActiveConnection的属性外,它是不可见的并工作在后台.

Field对象是一列同类的数据.它提供了一种这样的编程接口,即能让你可以对单个单元的值以及基本

的特征如:类型和大小进行读和写.所有给定记录集的Field对象形成了一个Field集.如我将在后面向

你说明的, Fields集隐藏着一个引人注目的特性,它将再次证明Recordset对象的灵活性.

最后是Property对象.每个对象都有属性.提供的使用了ADO的对象可能是各种各样的.但不存在这样一

套属性即包含了所有可能的OLE DB提供者的静态的属性.因此,任何一个ADO对象都有静态和动态的属

性.第一套属性集:包括Name,Type,Value和Attributes,都可以通过如下的语法来访问:

obj.PropertyName

最开始的三个属性都是自我说明性的.Attributes是一种数字式的描述符,它是以按位的跟提供者的能

力有关的一些预定义的属性组合.(它类似于COM服务器的组件分类).

动态属性是跟潜在的提供者类型相应的.它们被归进Properties集合,并可以通过名字查询.

obj.Properties("propName")

The New Recordset Object

ADO 2.0包括一些新的功能(见图3).现在可以通过GetString方法把记录集扁平化变成一个字符串.

Set Variant = recordset.GetString(

StringFormat,

NumRows,

ColumnDelimiter,

RowDelimiter,

NullExpr

)

所有的参数都是可选的,如下代码段所示:

Dim RS As ADODB.Recordset

Dim CONN As ADODB.Connection

Set CONN = CreateObject("ADODB.Connection")

CONN.Open "PUBS"

sql = "select * from authors"

Set RS = CONN.Execute(sql)

MsgBox RS.GetString(, 10)

RS.MoveFirst

这个例程连向大家熟悉的SQL Server PUBS数据库,将所有的作者名字提取进一个记录集中,然后将

它的最开始的10行拉直变成一个字符串.除了在Visual Basic中外,由GetString返回的值是一个

BSTR类型的变量结构.

在调用GetString时,你可以以相当大的自由度来通过指定参数来安排数据的格式.例如:你可以改变缺

省的行和列的分界符并选择只提取指定数量的行.目前,StringFormat参数的值只能是adClipString,

它是StringFormatEnum 列举的唯一的成员.adClipString的值是2,它表示相继的参数是有效的,库

应该把它们考虑进去.NumRows是将从当前的位置开始提取的行数.NullExpr参数提供了一个选择性

的字符串,以便在记录集中遇到了NULL值时用该字符串来取代它以进行显示.

GetString方法是ADO中的对应着RDO中的GetClipString的部分.在使用GetString时应该注意一些事

情:首先,该函数返回扁平的数据.没有输出关于结构和描述标识的信息或者是任何类似于类型表一类

的东西.结果是,GetString可以用来进行单向的数据传送,而且没有什么方法可以读回这种类型的字

符串.第二,GetString向前移动记录指针,如果你要进行进一步的处理,就把它移回来.你也要记住缺省

情况下该方法是输出整个记录集.这就意味着你你不用担心当前的记录是个无效的记录.

ADO 2.0的记录集对象中还有些其他的不大的改变.一个是ActiveCommand属性,它返回源于给定的记录

集(如果有的的Command对象. ADO类型库定义了ActiveCommand作为一个一般类,而不是Command对象,

所以IntelliSense模块(包含于所有的Visual Studio 6.0产品中)在写一些使用属性的代码时,对你

没有用处.如果你添加如下的一行.

MsgBox RS.ActiveCommand.CommandText

到前面的代码段中,你将能得到产生该记录集的SQL声明的原文.

select * from authors

 

书签

书签是任意的值,它可以唯一地标识记录集中的一行.在你需要将一个引用存储到特定的记录时,

你通常需要使用书签.如你可能猜到的那样,如果你通过标准的Clone方法克隆一个记录集,那么书

签就被复制了.源于不同的数据类型的ADO提供的书签有微小的差别.如果记录集的行总是一个数据

库记录,那么书签就可能是记录的排列值.该定义应该是绝对一致的,因此你可以通过简单的算术操

作来比较书签.

然而,记录集的来源可以是任何地方, 它包含的数据也可以寻址到不同的对象.此外,ADO编程者不需

要知道数据的内部结构.他或她仅仅是通过由行和域组成的接口来进行利用它的.让我们来看一个例子.

Microsoft Active Directory Service Interfaces (ADSI)是起到的是一个OLE DB提供者的作用.因

此,它公开了ADO不同种类的对象:从Windows NT或Novell目录服务到任何Lightweight Directory

Access Protocal(LDAP)—可塑的目录服务.另外,整个ADSI体系结构在跟任何提供对其他的目录的访

问的服务的提供者相比时象是个无声的客户那样进行工作.除了这些对象执行众所周知的和预定义的

编程接口,这就是说,ADSI它自身是这么一个公开的对象即它不知道对于任何ADO应用程序的细节.

有一个书签是用来标识这些对象中的一个.ADO应用程序和可能的诸如ADSI这样的提供者都并不知道

它.书签必须由提供者直接并且完全地管理.ADO通过OLE DB的IrowsetLocate接口处理书签.如果提供

者支持书签,它必须恰当的执行该接口.

Dim vBookmark As Variant

RS.CursorLocation = adUseClient

vBookmark = RS.Bookmark

.

RS.Bookmark = vBookmark

这代码说明了在代码中正确使用书签的方法.使用支持书签的指针类型是件重要的事情.书签是

个可读写的属性,你可以赋给它以任意值,包括连续的字符.

RS.Bookmark = "Hello, world"

如果你想在Visual Basic 6.0运行时应在用程序的书签所用的参数有问题时向你发出消息进行提

示.但终究书签的实际值应该总是对用户的应用程序是不可见的.

如果你需要比较书签,你必须使用ADO 2.0中公开的Recordset对象提供的CompareBookmark函数.该

函数和Bookmark属性自身是直接映射到IrowsetLocate函数上.特别是接口的定位方法如:Compare,

GetRowsAt和GetRowsByBookmark,其名字具有自解释性.图4显示了这世界上用到了记录集对象的所

有新特性的最简单的Visual Basic代码.

记录集已经滤过了在ADO 2.0前的能力,但是2.0版还添加了查找和分类特性.基本上说来,Sort属性

影响了记录集树被访问的方式和被遍历的行的顺序.Filter属性决定哪些行是对用户可见的.使用的

掩码由一列布尔项或是由书签数组给出.你也可以用通配符.最后,Find方法基于查找变址域来检索

一行.Find方法的语法是:

Find (criteria, SkipRows, searchDirection, start)

查找的准则是一个按如下格式的字符串:

< Field Name> < operator> < Value>

所有的布尔操作符除Like外都是可能的.准则字符串举例如下:

City = ‘Redmond‘

and

Name Like ‘Bill*‘

这里也存在一些严格的语法要求.日期值必须附上一对#字符,单引号必须包围字符串.

Find命令行允许你在开始查找之前跳过给定数目的行.初始位置(和那个你开始跳的位置)就是

Start参数.searchDirctiion决定了查找是自顶向下还是自下到上的.你可以做一些事来加快查找

处理的速度.首先,为记录集设定adUseClient指针,将你想检索的域的Optimize 动态属性设定True.

FieldName.Properties("Optimize") = True

这种方式,可以更快的检索和访问域的内容.注意Optimize不是提供者的属性.它只是在ADO内部定义

和使用的.该属性仅在asUseClient指针被设置时被添加到Properties集合中.

 

记录集的延续和缓冲

几个月前,一个读者给我发了个EMAIL,这里面有个很奇怪的问题.他说 “我喜欢象记录集这样的

结构,并且我在任何时候都用它们.ADO记录集可以用来进行对数据的缓冲而不需要涉及到数据库

连接或者,甚至OLE DB提供者?”我迅速对此回答: “不,我认为你不能这样做.记录集无论如何都

需要一个连接.”

通过更进一步的思考,我觉得他说到了最重要的问题上.ADO记录集是个灵活的,最优的数据结构.它

们能跟OLE DB数据库很好的工作,但是遗憾的是你并不能在任何你需要一个强大的使用方便数据结

构的时候使用它们.但这里有个好消息.ADO 2.0给你提供了针对这一问题的新特性.Field集提供了

相当新的Append方法,它能在最初就创建一个记录集.

Dim RS As New ADODB.Recordset

RS.CursorLocation = adUseClient

RS.Fields.Append "Name", adBSTR

RS.Fields.Append "City", adBSTR

RS.Open

RS.AddNew

RS!Name = "DinoE"

RS!City = "Redmond"

这里是一个ADO记录集,它就没有数据库连接和OLE DB的概念.ADO记录集总归是个独立的对象了.要

紧的是你所选择的客户指针以及将你的新的域添加到记录集的Fields集中去.Append方法需要两个

非选择性的参数:域名和类型.如果你想要字符串的话那就用adBSTR.

如果是这样的话,想象使用它的应用程序是个直接的办法.如:你可以写代码读你的老的所有者文件

并将它们的数据转到记录集中.

这只是一个由磁盘文件创建记录集的方法.ADO 2.0增加了对记录集的延续性的支持.实际上,你有了

个名为Save的新方法,它带有两个参数:输出文件名和数据格式.

Dim rs As New ADODB.Recordset

rs.adUseClient

rs.Save "c:demo.rst", adPersistADTG

目前,只有adPersistADTG(也就是零)是允许的.只有打开的记录集才能执行存储.存储的数据也可

能会受你所使用的过滤器的影响.注意在记录集关闭前该方法不会关闭文件的.在这中间,文件是作

为只读方式访问的.这使得ADO应用程序用Save方法来创建平行且延续的缓冲变得更容易了.如果该

文件已经存在了,Save方法返回一个错误,并准备执行一个工作环境.而且,该方法将当前行的位置

移到第一个记录处.它将所有与记录集相关的东西(包括数据和和图表)都存到磁盘上.然而,它并不

存储连接和命令信息.这将Save和GetString区分开来,并使得应用程序能加载先前存入的记录集.

使用的方法是:

rs.Open "c:demo.rst"

再次说明:使用客户端指针对于你来说是重要的.

 

异步获取和事件处理

ADO 2.0也可以让你以这样的方式执行命令:即以异步方式获取数据.这发生在调用Open方法的语法中.

recordset.Open Source, ActiveConnection, CursorType,

LockType, Options

跟通常一样,Source参数被指定一个有效的SQL字符串(甚至可能是一个被存储的过程的名字),一个先

前被存储的文件的名字,或者是一个Command对象.Options参数限定命令,并且可能影响提供者处理它

的方式.特别是它可以设定值为adExecuteAsync或adFethchAsync.adFetchAysnc意味着Source命令必

须是以异步的方式执行,也就是说,该方法是立即返回的并且提供者在操作结束时激活一个事件.就另

一方面来说,adFetchAsync使得第一块中的那些行被同步地取出(跟通常一样).其大小由记录集中的

CacheSize属性中所存的值来决定.剩下的那些行被异步的载入.

当一个软件以异步的方式运行时,平台应该提供一种方式使得用户知道它什么时候结束.对于这问题

有不同的实现方法,Platform SDK使用的是同步对象,Microsoft Internet Explorer Remote Scripting

使用的是回调函数.ADO选择的是第三种方法,也许对RAD编程者来说是最合适的一种方法:事件.

这当中有两类事件:与Connection对象相关的事件(ConnectionEvent接口)和属于Recordset对象的

(RecordEvent接口)(见图5).在许多情况下你的程序将会在特定的操作发生的前后被通知.在一个

操作发生后引发的事件总是伴随着一个Error对象用以描述操作的结果.在操作前引发的事件使得

你可以控制下一步的指令的参数.可能到达你的应用程序的事件会太频繁.通过正确地设定一个事件

的Status参数,你可以避免更进一步的通知.

WillChangeField cFields, Fields, adStatus, pRecordset

CFields参数是在下一个Fields数组中的Field对象的数目.Fields包含着带有挂起的变化的Field对

象.相关的记录集是通过pRecordset参数维持的.adStatus是一个类型EventStatusEnum对象,它的值

通常设为adStatusOK.现将它设为:

Set adStatus = adStatusUnwantedEvent

如果你不想再要这些通知.

处理ADO事件可能是容易的,这将取决于你所使用的语言.如同你可能猜到的那样,用Visual C++时你

应该从库中得到连接指针并调用它的Advise方法.这要求你在你的代码部分有相当大的编程量,但将

给你提供很多灵活性.实际上,你可以定义一个简单的处理多个对象引发的事件处理.另一方面,所有

输出接口中的方法都必须被执行,即使是用最简单的语句也行:

return S_OK;

与Visual C++相比,Visual Basic 和Visual J++就节省了你一些工作.特别是Visual Basic要求你为

每个对象都写相应的事件处理,这是由于事件处理过程是基于名称的.使用WithEvents关键字是在

Visual Basic中获取事件的最好方法.(如果你用Visual C++的话,WithEvent所做的将比你用

Visual C++实现的功能可能有些不同)

Visual J++遵循的是略微有所不同的实现方法:它是源于Java语言的基于Listener的事件模型.

一般说来,你需要定义一个类执行输出接口并将这个类附在引发事件的类的实例上.这是最底限

度的要求. 通过对ADO 2.0内置的支持,WFC将它极大地简化了.它用起来跟用Visual Basic一样

简单,但远比Visual C++简单.

ConnectionEventHandler handler = new

ConnectionEventHandler(this,"onConnectComplete");

先前的片段在ConnectionEvent接口中定义了一个类以执行onConnectComplete事件.接着你将如

下的 “Listener”函数添加到对象的事件处理中.

Connection conn = new Connection();

conn.addOnConnectComplete(handler);

于是,对于同样的一个事件你有了多个处理.

 

分层目录结构指针和数据成形

如果你处理数据,那么很可能你需要将它从多个表中提取出来.在大多数情况下,你使用JOIN命令合并

相关表中的数据,特别是在你访问关系型数据库的情况下.任何由JION命令形成的记录集总是包含了

多余的信息.例如,如果你对一个作者的所有书籍感兴趣的话,你可以合并Authors和Title Author表

(我在这里指的是Biblio和PUBS数据库).在返回的记录集中,关于作者的信息在每一行中无效地重复.

你合并的嵌套次数越多或越复杂,消除多余信息对于你来说就越重要,分层目录结构指针允许你以基

于树形的逻辑来组织你的记录集.该过程也叫做数据成形,并且它可以以两种方式来实现.你可以用

类似于SQL的成形语言,或者你可以通过使用Visual Studio 6.0的相应的高级工具.我将在后面提供

一个例子.现在我们来看看成形语言.

就某方面来说,成形语言显得跟SQL语言类似.

SHAPE {select au_ID, Author from authors}

APPEND ( {select ISBN from [title author]}

AS chapter RELATE au_id TO au_id)

一般说来,SHAPE命令定义记录集,APPEND子句添加一个子记录集到它当中去.换而言之,记录集可以

作为一个域的其他任何数据类型使用.(见图6).父记录集和子记录集是通过域与域间的关系实现连

接,该关系同样需要一个名称.



图 6 APPEND 记录集

作为数据成形的结果,父记录集附加了新的一列.每行中的新的域都是指向定义在APPEND从句中所定

义的记录集.子记录集只列举那些au_id值跟父记录集中相同的域的是匹配的记录.在前面的代码中,

如果给出一个作者的ID,那么存在一个记录集,在它当中有一个叫Chapter的附加的域,它的Value属

性是指向一个子记录集,该子记录集中有来自于Title Autor表的所有所请求的域,在本例中,

是ISBN.图7举例说明了一些用于分层目录结构记录的定位的Visual Basic源代码.注意我是怎样

通过名称来访问Chapter域.为了得到实际的子记录集,还需要另外一个步骤:调用Value属性.

SHAPE命令也可以嵌套.这意味着你能用其他的SHAPE命令作为APPEND的内部命令.跟SHAPE语法打交

道如同手工书写SQL查询一样也是件烦人的事情.幸运的是,Visual Studio 6.0给你提供了高级的工

具它可以显著地简化SHAPE查询地定义.我将简要地说明这一点.

APPEND不是唯一一个你可以附在SHAPE命令上的从句. 你还可以改用一个COMPUTE从句来代替.COMPUTE

能让你对已有的行执行统计功能,或按一个或更多的域进行分组.其预定义的函数有SUM, AVG, MAX和

MIN.在最后的记录集里就给出了父行的这些操作的结果.而且,该记录集也包含了一个引用域,它指向

由COMPUTE处理的原始行所在实际表.是不是觉得不太明白?让我们来看看一个例子.

假设你有个客户定单表.一天,你想知道每个姓是以A开头的客户的定单的总数.

SHAPE {select custID, last name

from orders where last name

like ‘A%‘‘}

COMPUTE (SUM(amount)) AS

chapter

BY custID

图8说明了结果记录集.你有用户的ID,姓,和订单的总数,除了客户定单的列表引用外,其它的都是形

式非常简单的.



图 8 COMPUTE 记录集

为在较底的层次上(也就是说,没有用专用工具的情况下)实现数据成形功能,你得指定你的提供者为

MSDataShape.

Dim cnn As New ADODB.Connection

cnn.Provider = "MSDataShape"

cnn.Open "Biblio"

 

Visual Basic 6.0 数据环境设计器

Visual Basic 6.0的最引人注目的新特性之一就是Data Environment Designer数据环境设计器

(见图9).它是个一般的,设计时环境,它能让你可视化地安排三种类型的数据工具:连接,命令和记

录集.Data Environment也是个对象.你可以将它包括在你的Visual Basic工程中,并在你的代码中

引用它.(见图10).



图 9 Data Environment Designer

为添加一个实例到工程中,只需要选择Project|Add Data Environment.于是,你就可以象其它任何

对象那样来处理它了.唯一的区别在于它是是个在整个工程范围内都有效的对象,而不是专门针对一

个给定的窗体的.



图 10 Data Environment Object Model(数据环境对象模型)

Data Environment对象模型是由三个集合组成:Commands, Connections和Recordsets,当然还要

加上一些通用的属性如Object和Name.你可以把一个对象嵌入到你的工程组中,并用它来准备任何你

在运行时需要的数据连接和查询.Data Environment模糊了SQL查询语言和SHAPE语言的差别.你可以

简单的定义命令,并可以图形化地做到这一点.(见图11).也就是说,应该由下面的环境来做这事情.



图 11 创建查询

这里有向导,对话框和工具条按钮帮助你快速的创建任何类型的记录集.例如,在图9中,你可以看到两

种不同的连接,它们是跟一个Microsoft Access数据库(通过针对ODBC的OLE提供者)和一个SQL Server

表连接.从设计者的立场来看,两个记录集的创建方式是完全相同的.嵌套命令对象的存在造成了这里

的分层目录结构的记录集.你可以通过上下文菜单添加子命令,如图12所示.定义一个新的连接如同遵

从图13中的指令一样简单.



图 12 添加一个子命令



图 13 创建一个新连接

你可以用多种方式在Data Environment Designer环境下创建一个对象.例如,你可以设定从新的

ADODC控件到MSFlexGrid的任何数据觉察控件的DataSource属性.第二,你可以利用它的对象模型

并直接访问任何预定义的连接,命令和记录集.你也能添加一些代码,它对ADO对象和DataEnvironment

的初始化和终止作出响应.为做到这一点,只需要在Project View窗口中选择DataEnvironment对象,

并切换到代码视(见图14)



图 14 处理ADO事件

在Visual Basic 6.0中, Data Environment Designer跟DataView窗口相配合发挥作用,IDE工具允

许你查看任何数据源的内部结构同时也可以创建新的数据连接.

 

在不同的环境下编写ADO

当用Visual Basic 6.0编写ADO时,你不能忽视Hierarchical FlexGrid(HFlexGrid)控件,见图15所示.



图 15 Hierchical FlexGrid

它显然比原来版本的Visual Basic的FlexGrid控件好得多.它得新特性如名称暗示的那样:内置对

分层目录结构的记录集的支持.图16显示了一个简单的基于Visual Basic 6.0的程序,它用到了

HFlexGrid控件并概述了我目前所涉及的所有的ADO的特性.在该例子中,我定义了一个

DataEnvironment对象并用它来从包含了一个作者的所有的书的Biblio中获取一个记录集.

Data Environment键和HFlexGrid控件间缺少的连接可在该片段中找到.



图 16 演示程序

Private Sub cmdDataEnv_Click()

Set hflex.DataSource =

DataEnv

Set hflex.DataMember =

DataEnv.RecordsetName

End Sub

该代码自动定义了DataEnvironment对象,在早先我是将它定义来作为HFlexGrid控件的数据源.

HFlexGrid控件也可以成功的用于显示平面记录集并且在功能上是跟原来版本的Visual Basic一样的.

可以通过新的名为MsAdoDC.ocx的ActiveX控件来使得ADO的功能可用.它看起来象早先的但目前

仍然被支持的Data控件.ADODC被假定为在你喜欢上它后将取代Data控件.如你可能猜到的那样,

ADODC具有Data控件中所没有的特性.它是基于OLE DB的并公开了ADO 2.0编程接口.



图 17 ADODC控件

在Visual Studio 6.0中,Visual InterDev是个主要的Web开发工具.它有两个专门的特性: Data

Environment对象(相同的东西在Visual Basic中也能被找到)和Recordset设计时控件.该思想是

简单的,添加Data Environment对象到工程中,定义你的数据连接,查询和完成其它你所需要的任

何工作.当你完成后,你将Recordset设计时控件插入到你的Active Server Pagers中,并从Data

Environment集合--Recordsets, Commands, Connections中提取你所需要的数据对象(记录集,

命令和连接).

< script language="JavaScript" runat="server">

function InitRS()

{

thisPage.createDE();

var rsTemp = DE.Recordsets(‘authors‘);

rsAuthors.setRecordSource(rsTemp);

rsAuthors.open();

}

先前的代码段说明了一个JavaScript过程,它恢复一个记录集并为在页面中进一步使用它而打开它.

在C++下使用ADO,并且通常使用源于C++的高级对象模型,常常引起下面的问题即需要将方法返回

的Variant类型转换成语言的本地类型.由于这个原因,ADO 2.0公开了附加的叫做IADORecordBinding

的接口,使得你将记录集的特定类型绑定为C++类型.这可以通过如下步骤来完成:

定义一个从CADORecordBinding派生出来的类并在头文件irsint.h里声明它.这个类必须有如下三个

方法: BindToRecordSet, AddNew和Update.

将尽可能多的所需要的数据成员添加到类中以映射记录集的域.通过使用其中一个如下的预定义的宏

将每个记录集的域跟它相应的数据成员绑定起来, ADO_VARIABLE_LENGTH_BINDING_ENTRY,

ADO_FIXED_LENGTH_BINDING_ENTRY和ADO_NUMERIC_BINDING_ENTRY.所有这些对宏的调用必须由

下面的给包围起来.

BEGIN_ADO_BINDING

END_ADO_BINDING

当你得到一个记录集,调用BindToRecordset,将其指针传给你的类.

现在,根据你的需要对记录集进行处理.重要的是从现在开始你可以对你自己的数据成员而不是对

记录集的源数据进行处理.每次你移动当前位置的时候,数据会被自动地提取出来并放在C++地类

实例变量中.

在Java语言中也有同样的问题,在Visual J++ 6.0中是通过在Field中引入附加的方法来解决这一

问题.

在Visual J++ 6.0环境下,在Java语言中使用ADO进行数据访问已变得简单了.新的Visual J++所基

于的WFC包括了一些特殊的专门用于数据管理的类.结果是在Java语言中编写一个数据库应用程序变

得比以前容易多了.

让我们来回顾一下Visual J++1.1的情况.你可以调用ADO 1.5对象模型,但是你必须通过COM来实现

这一点.由于Java/COM集成模型的因素,你需要在你的基于Java的程序和希望的COM服务器之间有一

套接口类.以这种方式,Microsoft Java Visual Machine能挂接这些接口并将它们重定向到实际的服

务器上.你也需要一个象Java Type Library Wizard(Java类型库向导, javatlb.exe 或 jcom.exe)

的工具来帮助你来生成所有的包装类.最后一步是调用Visual J++ Database Wizard来完成工作.

使用Visual J++ 6.0和WFC是完全不同的情况.现在类库公开了三种对象,分别叫做: Connection,

Command和Recordset.它们的方法和属性跟我在早些时候讨论的ADO对象模型是一一对应的.ADO事件也

可以遵循典型的收听者JDK模型来处理.

最重要的是,编程者的工作只是编写Java代码—不需要考虑工具,向导和中间类.下面是一个如何建立

一个连接并打开一个记录集的例子.

void openDataConnection() {

m_con = new Connection();

m_rs = new Recordset();

m_con.setConnectionString(

"Provider=MSDASQL.1;UID=sa;

PWD=;DATABASE=pubs;DSN=SQLS;SERVER=(local);" );

m_con.setCursorLocation(

AdoEnums.CursorLocation.CLIENT );

m_con.open();

m_rs.setActiveConnection( m_con );

m_rs.setSource( "select * from authors" );

m_rs.setCursorType( AdoEnums.CursorType.STATIC );

m_rs.setCursorLocation(

AdoEnums.CursorLocation.CLIENT );

m_rs.setLockType( AdoEnums.LockType.OPTIMISTIC );

m_rs.open();

}

Visual J++也为你提供了四个面向数据的组件: DataSource, DataBinder, DataNavigator和DataGrid.

DataSource实施一个数据库的连接,而DataBinder实现的功能多少象一组UI控件的粘合剂.这些组件在当

前行的位置变化时会自动刷新.DataBinder是跟特定的DataSource相连.DataNavigator跟Visual Basic

的Data控件很相似(即使是用户界面也很类似),而DataGrid是一个典型的基于数据源的栅格.

这是一个概述,它是关于这些内容:基于Java语言的WFC类库关于在使用ADO 2.0进行数据访问的情况下的

相关内容.Visual J++也使用向导来使得用Java开发框架数据库应用程序变得容易.

 

Web端的ADO

去年当Internet Explorer 4.0发布时,很少有编程者真正领会到数据绑定特性的重要性.该特性看起来

太棒了,但是它依赖于Internet Explorer 4.0并且缺乏适当的例子和应用程序.数据绑定是一项基于Web

的技术,它能让你不离开当前页面去访问远程的数据源.换而言之,数据绑定是数据觉察控件的Web版.如果

你使用它,你能让数据绑定HTML标签.举例来说吧,你可以将一个< TABLE> 连接到一个特定的数据源并且

可以让行和列在记录实际上进来的时候进行异步的添加.一旦你建立一个绑定,你(和你的用户)不再需

要做其他的事情.

数据绑定是通过一个叫做Data Source Object(DSO)的模块来实现的,它实际上是一个ActiveX控件,它

起到的大致作用是一个介于数据库和Web页面的代理.Microsoft为Internet Explorer 4.0提供了两个

DSO: Tabular Data Control (TDC) 和 Advanced Data Control (ADC).TDC只能处理基于文本的数据,

而ADC能连接到任意ODBC源.为有个初步的认识,你可以参考Rich Rollmann (MIND, July 1997)写的文

章"Data Binding in Dynamic HTML".现在,ADC演变为RDS—它是Web端的UDA和ADO的一部分,它被认为

是专门用于Internet的.

其中一个RDS是一个名叫RDS.DataControl 的ActiveX控件,你可以将它放到HTML页面里.通过使用数据

绑定技术,你可以在客户端有效地管理记录集.你可以对行进行定位,分类,刷新而不需要进一步跟服务

器联系.只有在刷新挂起的变化时你才要回到服务器上.RDS在客户端对数据进行缓存并极大地减少了

对往返行程的需要.下面的代码行显示了如何在HTML页面中使用RDS来填写一个表单.

< object id="rds"

classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33">

< /object>

< table datasrc="#rds">

< thead> < tr>

< th> First Column< /th>

< th> Second Column< /th>

< /tr> < /thead>

< td> < span datafld="FieldName1"> < /span> < /td>

< td> < span datafld="FieldName2"> < /span> < /td>

< /table>

 

RDS数据工厂

并不是所有的RDS都在客户端.当你通过VBScript或Jscript调用RDS ActiveX控件的方法时,你实际

上最后调用的是RDS Data Factory Server (RDSServer.DataFactory对象),它是位于Web的服务器

空间的.由于这个因素,数据绑定可以将你绑定到一个相容的浏览器上或一个激活了RDS的服务器上.

RDS Data Factory通过ADO接口以访问下面的数据源的方式来获取请求并完成它们.(见图18).它只是

完成查询和更新的工作.



图 18 RDS的体系结构

目前,RDS被定义为ADO的一部分并成为整个UDA体系结构的一个基本的组件.ADO 2.0加强了RDS方面的

功能. 对RDSServer.DataFactory对象进行了改进以支持一层自定义的代码,该代码的目的是添加合

法性验证的能力和访问权限控制.这个被叫做Handler的新模块可以在执行时对命令字符串和连接参

数进行修改.一个Handler可以被一个初始化文件驱动并且通过添加如下的句子来调用:

Handler=progID,arg1,arg2,…, argn

在记录集连接字符串中,需要用ProID来标识Handler,它是执行IdataFactoryHandler接口的COM服务器.

Microsoft提供了缺省的名为MSDFMAP.Handler的Handler,它是在msdfmap.ini文件中规定的.如果你

自己想的话,你可以编写自己的Handler(处理).下面是一段对msdfmap.ini文件的摘录,你可以在你的

Windows目录下找到它:

[connect AuthorDatabase]

Access=ReadOnly

Connect="DSN=MyLibraryInfo;UID=MyUserID;PWD=MyPassword"

[userlist AuthorDatabase]

Administrator=ReadWrite

[sql AuthorById]

Sql="SELECT * FROM Authors WHERE au_id = ?"

这意味着任何对AuthorDatabase的连接必须总是有只读权限并且总是要输入特定的字符串.名为

Administrator的用户有读/写权限,并且该设置部分覆盖了先前的一个.所有其它的用户仍然只有

只读权限.sql部分指向一个名为AuthorById的过程.每次它被调用时,它必须被特定的声明所取代,

并且?符号必须被第一个过程参数所取代.

rds.Handler = "MSDFMAP.Handler"

rds.Server = "…"

rds.Connect = "Data Source=AuthorDatabase"

rds.SQL = "AuthorById(16100)"

该代码向你说明了你如何在VBScript中通过Handler来调用RDS.RDS控件也可以无缝地运用到桌面应

用程序中.它最初的目标是基于Web,通过RDS和Dynamic HTML,举例说,来形成一个真正引人注意的套

件.特别是你可以编写自主式的HTML组件(scriptlet)来在< TABLE> 元素中给出一个记录集.关于这方

面的例子见MIND的1998年10的Cutting Edge栏目.

 

多维的ADO

ADO 2.0也扩展了OLE DB和关于On-Line Analytical Processing (OLAP)的Universal Data Access

的原则.它是通过引入MultiDimensional ADO (ADO MD)来做到这一点的. MultiDimensional ADO

(ADO MD)是一套COM对象,使用它可以使多维的数据管理变得更容易.

ADO MD并不局限于ADO对象模型并且包含了专门针对多维数据的对象,如cube.在ADO MD的外壳下面

运行的是一个OLE DB提供者,它是跟符合OLAP规范的OLE DB相容的.ADO和ADO MD是相关的但不是相

同的.两者都对记录集起作用,即使这些记录集对于ADO是扁平的,或是双向的数组,或者对于ADO MD

来说是n维的对象,其结果都是一样的.

ADO MD的对应着记录集的相应部分叫做cellset.它是从源于关系型表的数据中提取出来的.如果你

是的确需要的话,你可以在同一个工程中同时使用ADO和ADO MD.要参考例子,请看MSDN文档.

 

小结

这篇文章并没有试图提供所有ADO对象的对所有方法和属性的详尽的描述.就这方面有相应的文档

和不错的书.我在这里的初衷是试图向你介绍一些ADO在企业和分布式系统方面的新特性.ADO 2.0

超出了RDO和DAO.使用ADO,你可以通过RDS在Web上方便地传送数据,并且重要的是你拥有了一个对新

的数据源和自己定制的数据源很容易进行扩充的开放式体系结构.如果你考虑到ADO开发工具,你也不

必对此担心.ADO 2.0被很好地集成到Visual Studio 6.0中.Visual Studio的所有组件都提供了高级

的UI工具以使数据集成尽可能的平滑.使用Visual Studio 6.0,ADO的确可以建立了基于Windows的系

统的DNA(分布式互连网应用程序).
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值