VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)
首页 - 经验之谈 - 用VB和RDO访问SQL Server
发表评论(0)作者:, 平台:, 阅读:15518, 日期:2000-03-12
用VB和RDO访问SQL Server


远程数据对象 (Remote Data Object ,简称RDO) 是位于 ODBC API

之上的一个对象模型薄层,它依赖 ODBC API、选定的 ODBC 驱动程序以及

后端数据库引擎实现大部分的智能和功能,因此短小(大约250 K)、快速

、强健。RDO具备基本的ODBC处理方法,所以可以直接执行大多数ODBC API

函数。RDO包含在VB4.0/VB5.0企业版中,由一个动态连接库MS RDO32.DLL

实现。 当VB版本还只是2.0时,从VB访问MS SQL Server的手段只有API函

数集(VBSQL/DBLib或 ODBC API)。VB发展到3.0时,其DAO/Jet (Microsoft

Access的数据库引擎)功能非凡,具备面向对象的程序接口,但处理速度尚不

尽如人意。另一方面,尽管VBSQL/DBLib、ODBC API在速度方面性能很好,但

它需要花费很大的精力进行繁琐的编程工作。

当MS SQL Server发展到6.0、VB发展到4.0时,开发了新的对象模块,由此产

生了RDO,可以访问SQL Server、Oracle;RDO2.0则是由VB5.0提供的。值得

一提的是,它实现了“事件驱动型数据库访问的编程方式”。

要讨论RDO,就必然要谈到DAO。DAO/Jet是为了实现从VB访问Access数据库而

开发的程序接口(对象)。RDO是从DAO派生出来的,但两者很大的不同在于

其数据库模式。DAO是针对[记录(Records)]和[字段(Fields)],而RDO是作为

[行(Rows)]和[列(Columns)]来处理。也就是说DAO是ISAM模式,RDO是关系模

式。此外DAO是访问Access的Jet引擎(Jet是ISAM)的接口,而RDO则是访问

ODBC的接口。

可见,RDO是综合了DAO/Jet、VBSQL/DBLib以及ODBC的优点的对象(Object)。

需要强调的是,RDO是包裹着ODBC

API的一层薄薄的外壳,被设计成在后台(服务器端)有数据库存在的前提下

运行,同时也是针对SQL Server和Oracle而特别设计的。

RDO的优势在于它完全被集成在VB之中。此外,直接访问SQL Server存储过程

、完全支持T?QL、T?QL调试集成在开发环境中、Visual Database Tools的

集成化等,也是RDO的长处。


RDO之后是ADO


Microsoft公司已宣布今后不会对VBSQL/DBLib进行升级,而ODBC

API函数一级的编程方式也不为人们所喜爱。RDO自身今后将被以ActiveX技术

为基础的ADO(ActiveX Data O bjects)所替代。Visual

Basic下一个版本中由哪一个作为标准配置还不很明确,但这一发展趋势已经

很清楚。 为什么需要ADO呢?RDO是以ODBC为基础,而ADO则是基于全新的OLE

DB技术。ODBC本身是以SQL Server、Oracle等关系数据库作为访问对象;OLE

DB则不仅限于此,而将是可以对电子邮件、文本文件、复合文件、数据表等各

种各样的数据通过统一的接口进行存取的技术。DAO、RDO当然不会一下子消

失,但随着新技术的利用,它们的作用将逐步淡化。


RDO的对象模块图


要正确地使用RDO,有必要对其对象模块结构加以理解。在RDO的对象和集合

中,有很多对数据库的状态和设定进行操作的属性(Property),以及对数据

库进行操作的方法(Method)。利用这些,从RDO2.0起就可以开发事件驱动的

数据库应用程序。

RDO对象与VB中其他对象的概念相同。与VB用的ActiveX控件(以往称为

Custom Control或OCX、VBX)相似的是,RDO也带有属性和方法;但同

Spread、InputMan等普遍应用的ActiveX控件不同的是,RDO没有自己的用户界

面,因而可以和VB标准的Timer控件归为同一类。当然也可以将RDO看作调用

ODBC API函数,进而对后台数据库操作加以控制的对象。在RDO的属性和方法

中,? ??对单个的ODBC API函数以及一连串API函数的调用。


1. rdoEngine对象

最初调用RDO对象以及RDC(远程数据控件)时,自动生成rdoEngine对象的附

带事件(incident)。rdoEngine用于对RDO全局属性的参数、选项进行设置,

是在RDO的阶层结构内处于最上层的对象,包含了所有的其他对象。

rdoEngine对象与DAO/Jet不同,虽然被多个应用程序共享,但体现rdoEngine

对象的设定值的属性却并不共用,而是在各自的应用程序的程序界面中对其分

别加以设定。这些设定值对其他使用RDO以及RDC的应用程序没有任何影响。

rdoEngine不是集合的要素,而是重新定义的对象,rdoEngine对象不能被追加

作成。


2. rdoEnvironment对象

RDO对象在自动创建rdoEngine对象时,将rdoEnviroment对象的初始值生

成并保存为rdo

Enviroments(0)。一般情况下,应用程序中不必追加rdoEnvironment对象

,大多只需对已有的rdoEnviroments(0)进行操作就可以了。只有在支持

一个以上事务(Transaction),需要将用户名和口令信息分别处理的情况

下,利用rdoCreateEnvironment方法将特定的用户名和口令值作成新的

rdoEnvironment对象。在这个方法中可以指定固有名、用户名和口令,如

果所指定的值与rdoEnvironments集合的已经存在的成员名称相同,会产

生错误。新建的rdoEnvironment对象自动追加在rdoEnvironments集合的

最后。调用rdoCreateEnvironment方法时,其name参数可以是长度为0的

文字列,这时新的rdoEnvironment对象将不会被追加在rdoEnvi

ronments集合之中。

3.rdoConnection对象 rdoConnection对象用于同SQL Server的连接管理

,下面是与SQL Server连接的例子。

第一步用New关键字声明一个rdoConnection对象: Dim Cn as New

rdoConnection

由此生成独立连接对象,这时它还不是rdoConnection集合的成员。在

具体连接到SQL Server等之前,有必要设定rdoConnection集合的属性

。对此,使用With关键字编程效率会更好。

With CN

.Connect = “Uid = ; Pwd = ; Database = Pubs; DSN = MyPubsDSN;



' 设定Cursor类型

.CursorDriver = rdUseNone

'设定登录超时

.LoginTimeOut = 10 End With


用RDO与SQL Server连接和断开


RDO接口没有自动管理同SQL

Server的连接和断开的功能,需由程序员自己加以判断。这里需要注意的

是对连接和断开时机的管理,因为同时有过多的连接将会造成服务器负

载过重。而且,对SQLServer而言,一个连接只能同时支持一个操作,当

同时进行记录的读出和更新时,需要分别对其各自的连接加以确认。在

一定条件下需要保持连接;反之,当操作完成以后,又需要立即将连接

断开。如果能正确地设计好连接和断开的时刻,就有可能确保扩展更多

的用户数(客户/服务器开发环境中,与数据库项的可扩展性一样,客户

数扩张的可能性也非常重要)。 由于RDO是基于ODBC,它通过ODBC的数

据源名(DSN: Data Source Name)与SQL Server相连。DSN的信息可以基

于文件,也可以在连接时通过传递的参数指定。不过,一般来说,不推

荐基于文件的DSN,因为一旦DSN文件被破坏,或者被误删除,设定起来

就非常麻烦。通过 DSN对SQL Server的连接方式一般有以下几种: ?


查询ODBC的登录信息中是否存在有效的DSN:可以通过ODBC

API的SQLDataSources函数取得DSN条目的列表,不过这种办法实用性不大。 ?


用rdoRegisterDataSource函数生成新的DSN:虽然多少有一些麻烦,但比

查询ODBC 登录信息要好一些。 ?


与已有的DSN相连:需要手工设置DSN,而且安装应用程序之后还需个别地

设定DSN,对用户来说不是很友好的解决方法。 ?


从应用程序中自动启动控制面板的ODBC管理applet,向用户说明DSN的设

定方法:有的应用程序就是用这种方法进行设定,但仍不是很实用的办法。 ?


将SQL Server名保存在Windows

Registry或INI文件,在连接时对rdoConnectio

ns对象的属性作如下设置:在初次运行应用程序,或者找不到指定的SQL

Server名的情况下,要求用户进行输入,并设置在Windows

Registry或?INI文件中。通常,SQL

Server名不会改变;当连接出现错误时,可以像初次运行应用程序时那样

,为用户提供指定SQL Server 名的对话框,这种做法最为理想。 ?


利用ODBC3.0驱动支持的功能在连接时实际指定DSN文件:作安装程序时包

含这个DS N信息的文件,随同程序一起安装。


此外还有其他一些DSN的连接方法,这里不作一一描述。

这些方法中具体哪一个最实用,需根据各种应用程序的安全性要求、安装

方法等进行考虑加以选择,一般是组合上述方法的其中几种来编程。也

就是说,用DSN文件的方式来作实际上的连接,而同时采用对DSN文件是否

存在进行检查、不存在时要求用户输入SQLServer

名、用户名、口令并即时自动生成的方法。 实际的连接除了RDO

1.0的OpenConnection方法以外,也可以用RDO 2.0中新增加的Esta

blishConnection方法,我们将在下期中介绍这些方法。此外,下面我们

还将通过实际代码向大家继续介绍綝SN对SQL

Server的连接方式、数据的读取、追加、更新、删除等内容。


实例


在本系列文章的上篇刊文中,我们介绍了RDO的发展、优势、对象模块、趋势

,以及通过RDO与SQL

Server的连接和断开等内容,本期将通过实例来介绍RDO的应用。


OpenConnection方法的调用方式如下:

[调用方式:OpenConnection(dsName, Prompt, ReadOnly, Connect)]

其中参数说明如下:

?

dsName,指定登录在系统中的DSN条目。当用Connect参数指定DSN值时,

dsName必须为“”(长度为0的文字列)。

? Prompt,当设置为rdDriverPrompt常数时,可以在不能与SQL

Server连接的情况下,激活ODBC设置对话框来设定DSN。只是最好不要让

一般用户随意更改这个设定。通常情况下,正确的处理方法是设定为rdD

riverNoPrompt,利用Error处理程序编程进行处理。

?

ReadOnly,若需要通过连接对数据进行更新时设定为False。若没有数据

更新的必要则设为True,这时因为ODBC驱动没有对数据更新进行管理的需

要,可以提高程序效率。

?

Connect,向ODBC驱动管理器传递所有的ODBC连接参数。可以省略dsName

,只通过Connect参数进行包括用户名、口令、缺省数据库、DSN(此时

dsName参数的值无效)等全部参数值的传递。


下面是OpenConnection方法的一个实例。

设定的DSN为MyDSN:

Dim Cn As rdoConnection

Dim En As rdoEnvironment

Dim Conn As String

Conn = “DSN = MyDSN; UID = Jacob;” & “PWD = 123456;

DATABASE = MyDb;”

Set Cn = En.OpenConnection(“”, rdDriverPrompt, False, Conn)

在这个例子中,dsName是空

“”(长度为0的文字列)。DSN情报从Conn参数中所含的DSN =

MyDSN取得。

OpenConnection方法中也可以通过变量来进行参数传递:

[变量名:=值]

Set Cn = En.OpenConnection(Prompt := rdDriverPrompt, ReadOnly

:= False, Conn ect := Cnn)

虽然这多少有一些多余的代码,但毫无疑问会使程序维护工作容易得多? ?

EstablishConnection方法

EstablishConnection方法的调用方式如下:

调用方式:EstablishConnection(Prompt, ReadOnly, Connect)

本文前面对独立(stand

alone)的连接对象(rdoConnection)作了说明,EstablishCon

nection方法可以用于这种情况。EstablishConnection方法同OpenConne

ction方法很相似,被用于停止状态或独立的rdoConnection对象。

这里以独立的rdoConnection对象为例说明与SQL Server的连接。

Public WithEvents Eng As rdoEngine

Public WithEvents Cn As rdoConnection

 

Private Sub Form_Load()

Set Eng = New rdoEngine

Set Cn = New rdoConnection

With Cn

.Connect = “UID = ; PWD = ;” & “DATABASE = pubs; DSN =

biblio”

.LoginTimeout = 5

.EstablishConnection rdoDriverNoPromt, True, rdAsyncEnable

End With

End Sub

在这个例子中,Form_Load函数对rdoEngine和rdoConnection对象进行初

始化。这里有一点需要注意,rdoConnection对象是处于独立的状态之下

,即使是处于未连接状态也可以设置属性的值。

接下来是rdoConnect对象的事件处理程序。从RDO

2.0起可以实现异步方式(rdAsyncEn

able),EstablishConnection就设定为该值。在异步状态下,不必等待

与数据库的连接,程序可以迅速从Form_Load函数中退出。

然后是BeforeConnect事件,该处理在与数据库的连接开始以前被激发,

此时不能进行有关终止连接的操作:

Private Sub Cn_BeforeConnect(ConnetString As String, Prompt As

Variant)

MsgBox “正在连接” & ConnectString, vbOKOnly, “连接前”

End Sub

连接完成之后的事件处理:

Private Sub Cn_Connect(ByVal ErrorOccurred As Boolean)

Dim M As String

If ErrorOccurred Then

For Each er In rdoErrors

M = M & er & vbCrLf & M

Next

MsgBox “连接失败” & vbCrLf & M

Else

MsgBox “连接成功”

'这是确认连接状态的测试代码

Cn.Excute “use pubs”

End Sub

RDO连接处理结束后,在该事件中确认连接成功与否。连接成功的情况下

ErrorOccurred

返回False,失败时为True,由此可以对rdoErrors集合进行检测:

Private Sub Eng_InfoMessage()

For Each er In rdoErrors

Debug.Print er

Next

RdoErrors.Clear

End Sub

不能与SQL Server连接的原

因多种多样,有可能是由于对数据库的访问权限、网络连接问题、数据库

表的信息错误、SQL Server同时连接的许可数、资源不足等等,具体情况

需要与网络管理员商量。

断开连接的操作非常简单,但又很重要,因为RDO不提供自动断开的功能



Cn.Close

Set Cn = Nothing '释放对象所占的内存资源

En.Close

Set En = Nothing '释放对象所占的内存资源

VB是对象语言,Form、ActiveX控件也都是对象。使用对象后必须养成将

对象设为Nothing把它从内存中释放的编程习惯。这样可以预防很多不可

预测错误,往往程序中发生意义不明的错误时,其因就在于此。


数据的取得


与数据库连接成功之后,接下来就是取得数据。一般用OpenResultset方法取

得数据,这里首先需理解数据库中与此有关的[游标]概念。


 


一言以蔽之,游标[cursor]就是指向依据一定的条件从数据库中抽取的数据的多

个指针。也就是说游标是用作指向由数据库返回的数据的方法。

RDO

支持几种不同的游标库,其中每一种都有其特定的作用:向前滚动型的结

果集(rdOpenForwardOnly-

缺省值)、静态滚动型结果集(rdOpenStatic)、可滚动的查询结果集

(rdOpenKeyset)和动态可滚动的查询结果集(rdoOpenDynamic)。在使

用RDC的情况下,游标的值设定为ResultsetType属性;在使用RDO的情况

下,通过OpenResultset方法的Type参数进行设定。游标又分为[服务器端

游标]和[客户端游标],这需要根据程序的性质、处理内容的不同来选择

适当的方式。也有不使用游标的情况,比如当只进行数据读取时,使用

rdUseNone选项更为合适。

关于是否使用游标、使用何种游标,需考虑下面一些情况:


 

* 需要读取多少行数据?要读取全部或只是几行数据?

*

是否等待游标的创建完成?对于用户来说等待时间是否在允许范围之内?

*

用来保存已创建游标的系统资源(内存容量),在用户端或服务器端是

否充足?

*

从服务器端返回的结果怎样读取?有必要从当前行随意移动,还是从最初

到最后顺序读取为好?

* 游标的成员关系怎样定义?

*

数据如何更新?数据有没有更新的必要?是通过Execute方法将游标的数

据进行更新,或是由存储过程更新?

关于游标的详细介绍,请参照Visual Basic Books Online以及SQL

Server用户手册。这里介绍从RDO对象读取数据的几种方法:

*

rdoResultset对象,这是RDO的基本游标对象。与DAO的Recordset对象相

似,可应用于各种游标以及无游标的场合。可以通过rdoConnect对象或r

doQuery对象的OpenResultset方法创建rdoResultset对象。

*

rdoQuery对象,与DAO的QueryDef相似,在进行一次性查询时使用,用于

取代已经

过时的由rdoConnection对象的CreatePreparedStatement方法创建的rdo

PreparedStatement对象。rdoQuery对象直接调用ODBC的SQLPrepare。

* UserConnection对象,在VB5.0以及RDO

2.0中新增加的对象,这里不作详细阐述。它是一个非常优秀的对象,使

RDO使用起来更加容易。

* rdoTable对象,是rdoTables集合的成员,用来显示SQL

Sever上的一个表的内容。

编程中通常运用前3种方法中的某一种,而第4种一般不太用。

不论使用哪一种方法,都有必要给出SQL语句。在绝大多数的情况下,SQ

L语句是针对SQL

Server,从哪个表以何种条件读取哪一行的数据,如(SELECT * FROM

table1 WHERE field1 =

“condition”)。运行存储过程时用Name参数指定存储过程,程序能自动

判别是RDO

2.0的还是存储过程的SQL语句。在以存储过程为核心的情况下,利用Use

rConnection将存储过程作为方法来处理最为妥当。


 

下面给出一个运行rdoConnection对象Cn的OpenResultset方法的例子。

Dim Rs As Resultset

Set Rs = Cn.OpenResultset(name:=“SELECT * FROM Authors WHERE

Year_Born=1966”)

If Rs.RowCount > 0 Then

MsgBox Rs.RowCount & “条记录取得完毕。”

Else

MsgBox “没有取得任何记录。”

End If

这个例子中用了name:=,OpenResultset方法,除了name参数以外,也可

以使用lock、

locktype、option等。下面是从Resultset中将数据读取到ListBox中的方

法:

Do Until Rs.EOF

List1.AddItem Rs(“au_lname”)

Rs.MoveNext

Loop

存储过程虽然不在本文讨论的范围内,这里也简单地介绍一下。存储过程

基本上有以下4种类型:

{call myStoreProcedure} ' 没有参数的存储过程

{call myStoreProcedure(?)} ' 单一的输入或输出参数

{? = call myStoreProcedure(?)} ' 单一参数、有返回值

{? = call myStoreProcedure(?, ?)} ' 多个参数、有返回值

下面演示一个运行存储过程的例子:

sp_GetVendorCount参数是名称的条件,其返回值为符合该条件的记录数



Dim CPw As rdoQuery

Dim sSQL As String

SSQl = “{? = call sp_GetVendorCount(?)}”

这里“名称的条件”是输入参数,由ODBC驱动器进行自动识别。使用rdo

Parameters(n).Direction可以对返回值(rdParamReturnValue)、输入参

数 (rdParamInput)

、输出参数(rdParamOutput)和输入输出参数(rdParamInputOutput)加以

控制。但通常ODBC都会读入存储过程的定义式,并加以正确识别,所以绝

大多数的情况下不必使用这个参数。

Dim CPw = Cn.CreateQuery(“GetVendorCount”, sSQL)

代码生成名为GetVendorCount的rdoQuery对象,并将rdoQuery对象自动? 黾拥絩

doQueries集合中,以后可以重复使用。

现在将CPw对象的第一个参数指定为返回值:

CPw.rdoParameters(0).Direction = rdParamReturnValue

最后由Execute方法运行:

CPw.Execute

返回行的查询(存储过程中包含一个以上的SELECT)时,可使用OpenRes

ultset方法。运行后,可通过rdoParameters集合取得返回值:

If CPw.rdoParameters(0) > 0 Then

MsgBox CPw.rdoParameters & “数据取得成功”

Else

MsgBox “数据读取失败”

End If


数据的追加、更新、删除


对SQL语句已经

有一定了解的读者,应该比较熟悉INSERT、UPDATE、DELETE等语句。对? 趓

doConnection对象,虽然可以在OpenResultset的Name参数中直接代入

SQL语句,用Execute方法运行,但没有充分利用RDO对象的长处。在rdoR

esultset中有AddNew、Edit、Update、Delete、MoveNext、MovePreviou

s、MoveFirst、MoveLast方法,与DAO/Jet相似,用起来非常便利。

下面在先前的Resultset的例子中追加记录:

Rs.AddNew

Rs(“au_id”) = “111?6?992”

Rs(“au_lname”) = “Takenami”

RS(“au_fname”) = “Teruo”

'设定所有的field,通常调用Call SetField,在别的模块中设定

Rs.Update

与SQL语句中的INSERT相比起来,这种方法非常简单,而且代码可读性好

。记录的更新方法如下,与追加相似,所不同的只有最初的Edit方法:

Rs.Edit

Rs(“au_lname”) = “Takenami”

RS(“au_fname”) = “Teruo”

'设定所有的field,通常调用Call SetField,在别的模块中设定

Rs.Update

在实际应用中,字段的更新放在别的模块中,便于从AddNew、Edit两种处

理中都可以进行调用。

使用Delete方法删除记录时,当前行被删除。当前行可以通过MoveNext、

MovePrevious等Move方法以及Bookmark属性设定。

Rs.Delete '删除当前记录

这里需要注意的是当前记录被删除之后记录指针的位置。Delete执行后,

记录指针仍然指向已被删除的记录,也就是空的记录,对这个空记录进

行读写操作会产生错误。所以通常在Delete之后应立即执行ReQuery或Mo

veNext操作:

Rs.ReQuery 或 Rs.MoveNext

以上简单地说明了数据的增加、更新、删除方法,根据数据库的模式的不

同,增加、更新会变得非常复杂。另外,由于数据表的原

因,有时会使得删除操作变得复杂。通常与数据表的构造、相关性有关的

处理,为了使客户端的代码尽可能简洁,应在SQL

Server上创建触发器。有关触发器的内容已超出了本文讨论的范围,这里

不详细说明。本文介绍的只是一些基本的操作方法,RDO数据处理功能不

仅限于此,读者可在实际开发中进一步领会。


总结


RDO是开发数据库应用程序功能强大的对象方法,要真正做到应用自如,

需要付出很大努力。本文描绘了RDO基本的构成、功能、编程方式,希望

读者由此对数据库编程方式以及RDO的使用有更为充分的了解。如果需要

进一步研究,建议可以从以下几方面入手:

* SQL

Server(或Oracle)的功能,特别是存储过程、View、触发器、安全模式等;

* 数据库设计基础;

* SQL语句;

* Visual Basic的对象概念。

这些粗看起来与RDO没有什么联系,但实际上有助于对RDO的结构、原

理等基本技术的理解。换而言之,学习数据库编程的基本内容为大前提,

RDO或者ADO的应用不过是访问数据库的一种手段而已。