我使用activex data object(ado)是从1.5版本开始,那已经是一个非常古老的版本了。现在的版本中,许多东西都发生了变化。从每一次版本升级中我都学到许多新的东西。这些东西你不能全部从书本上找到,或者至少可以说,不能从一个地方找到。
我在这里精心选择和总结了这些ado开发要点和技巧。其中有些问题可能就是你一直念念不忘的问题;有些是你从来不曾了解的技术;还有一些只是分门别类地展示ado开发的知识精华。
一、共享连接对象
把连接字符串传递给command、recordset或者record对象时,每次你都是在隐含地命令ado创建一个connection对象:
dim rec1 as adodb.record
dim rec2 as adodb.record
dim rec3 as adodb.record
set rec1 = new adodb.record
rec1.open "localstart.asp", "url=http://localhost/"
set rec2 = new adodb.record
rec2.open "global.asa", "url=http://localhost/"
set rec3 = new adodb.record
rec3.open "iisstart.asp", "url=http://localhost/"
执行一些操作
rec1.close
rec2.close
rec3.close
set rec1 = nothing
set rec2 = nothing
set rec3 = nothing
为了节省资源,你应该先创建一个connection对象,然后把它传递给所有要求活动连接的对象。也就是说,上面的代码应该改成下面这种形式:
dim con as adodb.connection
dim rec1 as adodb.record
dim rec2 as adodb.record
dim rec3 as adodb.record
set con = new adodb.connection
con.open "url=http://localhost/"
set rec1 = new adodb.record
rec1.open "localstart.asp", con
set rec2 = new adodb.record
rec2.open "global.asa", con
set rec3 = new adodb.record
rec3.open "iisstart.asp", con
执行一些操作
rec1.close
rec2.close
rec3.close
con.close
set rec1 = nothing
set rec2 = nothing
set rec3 = nothing
set con = nothing
二、读取connectionstring属性
从任何已经打开的connection对象中,包括由recordset、command、或者record对象的activeconnection属性返回的connection对象,你总是可以读取到connectionstring属性。
dim com as adodb.command
dim rst as adodb.recordset
set com = new adodb.command
com.activeconnection = _
"provider=microsoft.jet.oledb.4.0;" & "data source=nwind.mdb;"
com.commandtext = "select * from customers"
set rst = com.execute
msgbox com.activeconnection.connectionstring
rst.close
set rst = nothing
set com = nothing
上述代码运行时,你将从消息框看到如下输出:
provider=microsoft.jet.oledb.4.0;
password="";
user id=admin;
data source=nwind.mdb;
mode=share deny none;
extended properties="";
jet oledb:system database="";
jet oledb:registry path="";
jet oledb:database password="";
jet oledb:engine type=4;
jet oledb:database locking mode=0;
jet oledb:global partial bulk ops=2;
jet oledb:global bulk transactions=1;
jet oledb:new database password="";
jet oledb:create system database=false;
jet oledb:encrypt database=false;
jet oledb:dont copy locale on compact=false;
jet oledb:compact without replica repair=false;
jet oledb:sfp=false
现在你就可以分析这个字符串,找出有关该连接的特定信息,比如当数据库被压缩时它是否会被加密(jet oledb:encrypt database属性)。
三、使用动态属性
connection对象的properties集合可用来设置供应商特有的选项,比如sql server的ole db驱动程序的prompt动态属性。
dim con as adodb.connection
set con = new adodb.connection
con.provider = "sqloledb"
con.properties("prompt") = adpromptalways
con.open
提示用户选择数据库
con.close
set con = nothing
上述代码运行时,用户将看到一个对话框,这个对话框允许用户选择要登录到哪一个数据库。
四、明智地选择游标位置
选择游标的位置时,你必须考虑对于当前连接来说哪些服务比较重要。
如果数据提供者的服务正是你所需要的,你应该使用服务器端游标。这些服务是数据源驱动程序提供的服务,它们通常具有非常好的可伸缩性。另外,通过保留服务器端游标,你无需象使用客户端游标那样总是把全部的数据发送到客户端。
另一方面,本地游标服务,例如microsoft数据形状服务for ole db,能够提供一些客户端游标特有的服务。要让这些服务能够起作用,数据必须发送到本地机器上,正如数据形状服务所要求的一样。
你可以用connection.cursorlocation属性设置游标的位置,但选择应该明智、慎重。
五、明智地选择游标类型
选择游标的类型与选择游标的位置同样重要。游标共有四种类型,每一种类型都有各自的优点和缺点。
static游标(静态游标)提供了数据在给定时刻的一个快照。在这种类型的游标中,数据改动(包括其他用户的数据增加或者删除操作)总是不可见。static游标用来制作报表很理想,因为制作报表需要有数据的一个一致的、不会变化的视图,但static游标不一定速度最快。由于数据的改变不会显示出来,对于每一个使用static游标的连接,服务提供者必须分别为它创建和维护一份给定时刻的数据副本。
forward only游标(只能向前的游标)与静态游标基本相同,不同之处在于你只能向前移动访问数据,但不能向后。与static游标相比,这个限制有利于提高性能,但它仍旧要求数据源维护一个数据的临时副本,使得其他用户对数据的改动不会影响你的数据。
dynamic游标(动态游标)允许你看到其他用户对数据的修改和删除操作,而且你可以在整个记录集之内自由地移动。与static和forward only游标不同,dynamic游标不要求数据源维护一份数据的静态映像,因此dynamic游标要比前两种游标快。
最后一种游标类型是keyset游标(键集游标)。keyset游标与dynamic游标非常相似,不同之处在于你不能看到其他用户新增的记录。在keyset游标中,其他用户删除的记录也将不可访问。和dynamic游标一样,keyset游标中你也可以看到其他用户的修改。keyset游标可能要比dynamic游标快,这是因为keyset游标不需要经常地去检查是否有新记录加入、是否有记录被删除(因为新增的记录不可见,被删除的记录变成不可访问)。
考虑每一个理由,然后再选择适合你的游标类型。
六、手工构造参数
当性能因素很重要时,请手工定义参数:
dim con as adodb.connection
dim com as adodb.command
dim par as adodb.parameter
dim rst as adodb.recordset
set con = new adodb.connection
con.open "provider=sqloledb;" & "server=localhost;" _
& "initial catalog=northwind;" & "user id=sa;"
set com = new adodb.command
set com.activeconnection = con
set par = com.createparameter ("categoryname", advarwchar, _
adparaminput, 15)
com.parameters.append par
set par = com.createparameter ("ordyear", advarwchar, _
adparaminput, 4)
com.parameters.append par
com.commandtext = _
"execute salesbycategory produce, 1997"
set rst = com.execute
执行一些操作
rst.close
con.close
set com = nothing
set rst = nothing
set con = nothing
采用手工方式定义参数之后,ado不必为了找出存储过程的参数列表而去查询数据源。当用户只执行一个查询过程且对存储过程执行时间要求不高时,这一点不是很重要;但是,如果用户要执行大量的存储过程,而且希望能够立即得到答案,那么这一点就很重要了。
七、用stream对象构造缓存
stream对象可以在没有物理数据源的情况下使用。你可以利用这个对象在本地机器的内存中创建缓存。用法很简单,只需先创建stream对象的实例,然后就可以开始写入数据:
dim str as adodb.stream
set str = new adodb.stream
str.open
str.writetext "这是文本信息,"
str.writetext "我们希望它保留在"
str.writetext "内存中。", adwriteline
str.writetext "这是第二"
str.writetext "行", adwriteline
msgbox "缓存中共有" & _
str.size & "个字符"
str.position = 0
msgbox "缓存内容: " & str.readtext
str.close
另外,你还可以用stream对象处理二进制数据,只需用write和read方法取代操作文本的writetext和readtext方法。把数据放入缓存之后,你可以用savetofile方法永久保存数据。
八、检查警告信息
connection对象的errors集合不仅用来报告数据提供者在执行某个操作时出现的错误,而且它还用于指示执行操作过程中出现的非致命性警告信息。
connection.open、recordset.cancelbatch、recordset.resync以及recordset.updatebatch方法,还有recordset.filter属性,都有可能产生警告信息。
要检测数据提供者的警告信息(或错误信息),请在使用上述任何方法启动某个操作之前调用connection.errors.clear方法;操作完成后,用errors集合的count属性检查是否存在任何警告信息。
九、事务嵌套
使用jet ole db provider时,你可以嵌套事务,最多五层。使用多层事务将赋予你空前的数据控制能力。
dim con as adodb.connection
dim ilevel as integer
set con = new adodb.connection
con.cursorlocation = aduseclient
con.open "provider=microsoft.jet.oledb.4.0;" _
& "data source=nwind.mdb;"
con.begintrans
改动 1
con.begintrans
改动 2
con.begintrans
改动 3
ilevel = con.begintrans
改动 4
msgbox "level " & ilevel
con.committrans
con.rollbacktrans
con.committrans
con.committrans
con.close
set con = nothing
在上面这个例子中,改动1和2将成功,改动3和4无效。改动4表面上已经被提交,但由于第三层事务回退,从而导致所有它里面的事务都被回退。
十、重视数据形状
我要为你介绍的最后一则技巧是不要轻视microsoft data shaping service for ole db的力量。data shaping(数据形状)允许你聚合多个sql语句,构造出层次型的记录集。在层次型记录集中,单个字段能够指向整个子记录集。
例如,如果有两个来自biblio数据库的表publishers和titles(biblio数据库是microsoft的一个示例,可以从这里下载),你可以按照下面介绍的方式构造sql命令,把它们连接到一个记录集。
select publishers.name, titles.title
from publishers
inner join titles on
publishers.pubid=titles.pubid
order by publishers.name, titles.title;
前面几个记录如下所示:
name (pub) title
————– —————————–
a k peters a physical approach to col…
a k peters colour principles for c…
a system pubns c plus plus reference card
a system pubns c reference card
aa balkema planning with linear progr…
aarp thesaurus of aging termin…
abacus access 2.0 programming bible
abacus advanced access programming
可以看到,这个记录集中存在大量的重复数据。利用数据形状技术,我们能够极大地减小结果数据的规模大小。下面的表被发送到客户端,并传递给数据形状游标服务。
name (publisher)
————————————
a k peters
a system pubns
aa balkema
aarp
abacus
title
————————————
a physical approach to color…
colour principles for computer…
c plus plus reference card
c reference card
planning with linear programming
thesaurus of aging terminology : …
access 2.0 programming bible
advanced access programming
在数据形状游标服务中,这两个表通过chapters字段类型连接成一个层次结构,chapters字段类型用来访问子记录集。
dim con as adodb.connection
dim rstpubs as adodb.recordset
dim rsttitles as adodb.recordset
dim sshape as string
set con = new adodb.connection
set rstpubs = new adodb.recordset
con.provider = "msdatashape"
con.open "data provider=microsoft.jet.oledb.4.0;" _
& "data source=biblio.mdb;"
sshape = "shape {select name, pubid " _
& " from publishers} " _
& " append ({select title, pubid " _
& " from titles} " _
& " as pubtitles " _
& " relate pubid to pubid) "
rstpubs.open sshape, con
do until (rstpubs.eof)
debug.print rstpubs!name
set rsttitles = rstpubs("pubtitles").value
do until (rsttitles.eof)
debug.print " " & rsttitles!title
rsttitles.movenext
loop
rstpubs.movenext
loop
rstpubs.close
con.close
set rstpubs = nothing
set con = nothing
运行上面的代码时,我们将看到如下输出:
a k peters
a physical approach to color…
colour principles for computer…
a system pubns
c plus plus reference card
c reference card
aa balkema
planning with linear programming
aarp
thesaurus of aging terminology : …
abacus
access 2.0 programming bible
advanced access programming
可以看到,数据形状的功能非常强大。
