zoukankan      html  css  js  c++  java
  • 一个常见数据库操作错误的分析(三)

    接下来再看看什么是ADO/OLEBD

    一致的数据访问技术——ADO/OLE DB

    潘爱民

    北京大学计算机科学技术研究所(100871)

    【摘要】本文首先介绍了Microsoft提出的一致数据访问技术——ADO/OLE DB,然后介绍了ADO的对象模型,以及ADO的高级特性,最后通过例子说明了ADO的用法。

    关键字:UDA (Universal Data Access)ADO(Active Data Object)ASP(Active Server Page)OLE DBCOM、自动化(Automation)

    Microsoft新近推出的一致数据访问(Universal Data AccessUDA)技术为关系型或非关系型数据访问提供了一致的访问接口,为企业级Intranet应用多层软件结构提供了数据接口标准。一致数据访问包括两层软件接口,分别为ADO(Active Data Object)OLE DB,对应于不同层次的应用开发,ADO提供了高层软件接口,可在各种脚本语言(Script)或一些宏语言中直接使用;OLE DB提供了低层软件接口,可在C/C++语言中直接使用。ADOOLE DB为基础,它对OLE DB进行了封装。一致数据访问技术建立在MicrosoftCOM(组件对象模型)基础上,它包括一组COM组件程序,组件与组件之间或者组件与客户程序之间通过标准的COM接口进行通讯。

    由于ADO建立在自动化(Automation)基础上,所以ADO的应用场合非常广泛,不仅可在Visual Basic这样的高级语言开发环境中使用,还可以在一些脚本语言中使用,这对于开发Web应用,在ASP(Active Server Page)的脚本代码访问数据库提供了捷径。本文将首先介绍ADOOLE DB的结构模型,以便读者了解ADOOLE DB之间的关系,然后介绍ADO的对象模型和ADO的一些特性,同时我们也将通过一些例子代码向读者展示ADO的用法。

    一致数据访问介绍

    随着网络技术和数据库技术的不断发展,现在的应用系统对数据集成的要求越来越高,这些数据有可能分布在不同的地方,并且使用不同的格式,例如关系型数据库、操作系统中的文件、电子表格、电子邮件、多媒体数据、目录服务信息等等。传统的解决方案是使用大型的数据库系统,把所有这些数据都移到数据库系统中,然后按照操作数据库的办法对这些数据进行访问,这样做虽然能够按统一的方式对数据进行各种操作,但这种间接访问方式带来了很多问题,比如数据更新不及时、空间资源的冗余、访问效率低等。

    Microsoft推出的一致数据访问技术则较好地解决了这些问题,它使应用可通过一致的接口来访问各种各样的数据,而不管数据驻留在何处,也不需要进行数据转移或复制、转换,在实现分布式的同时也带来了高效率。并且UDA技术在统一数据访问接口的同时,它的多层结构使数据使用方有了更多的选择机会,而它强大的扩展能力也给数据提供方留下了更多的扩展余地,这种开放型的软件结构使它具有极强的生命力,所以,这种技术从一推出便获得了广泛的欢迎,可以说,UDA技术是继ODBC之后的又一数据访问技术的飞跃。

    UDA技术包括OLE DBADO两层标准接口,OLE DB是系统级的编程接口,它定义了一组COM接口,这组接口封装了各种数据系统的访问操作,这组接口为数据使用方和数据提供方建立了标准,OLE DB还提供了一组标准的服务组件,用于提供查询、缓存、数据更新、事务处理等操作,因此,数据提供方只需实现一些简单的数据操作,在使用方就可以获得全部的数据控制能力。

    ADO是应用层的编程接口,它通过OLE DB提供的COM接口访问数据,它适合于各种客户/服务器应用系统和基于Web的应用,尤其在一些脚本语言中访问数据库操作是ADO的主要优势。ADO是一套用自动化(Automation)技术建立起来的对象层次结构,它比其它的一些对象模型如DAO(Data Access Object)RDO(Remote Data Object)等具有更好的灵活性,使用更为方便,并且访问数据的效率更高。

    1显示了统一数据访问的软件层次模型。

    1 UDA的层次结构图

    从图中我们可以看出,应用程序既可以通过ADO访问数据也可以直接通过OLE DB访问数据,而ADO则通过OLE DB访问底层数据。而且,OLE DB分成两部分,一部分由数据提供者实现,包括一些基本功能,如获取数据、修改数据、添加数据项等;另一部分由系统提供,包括一些高级服务,如游标功能、分布式查询等等。这样的层次结构既为数据使用者即应用程序提供了多种选择方案,又为数据提供方简化了服务功能的实现手段,它只需按OLE DB规范编写一个COM组件程序即可,使得第三方发布数据更为简便,而在应用程序方可以得到全面的功能服务,这充分体现了OLE DB两层结构的优势。

    由于OLE DBADO都以COM组件的形式实现,所以COM组件的各种特性也使得构建数据应用更为灵活,而不仅仅局限于一般的客户/服务器或Web应用模型,它既适合于以数据为中心的应用,也适合于多层结构的分布式软件系统。通过对COM组件的配置,我们可以建立各种复杂的应用系统。利用从COMDCOM的位置透明技术,我们可以很方便地建立分布式应用系统;利用MTS(Microsoft Transaction Server)运行环境,我们也可以在数据访问一层增加安全性控制,并利用MTS的对象管理使数据访问效率更高。所有这些功能都无须数据提供方编写代码实现,只需在DCOMMTS环境中进行常规的配置即可。

    可以说一致的数据访问技术的核心是OLE DBOLE DB建立了数据访问的标准接口,它把所有的数据源经过抽象形成行集(rowset)的概念。OLE DB模型主要包括以下一些COM对象:

    (1)    数据源(Data Source)对象。数据源对象对应于一个数据提供者,它负责管理用户权限、建立与数据源的连接等初始操作。

    (2)    会话(Session)对象。在数据源连接的基础上建立会话对象,会话对象提供了事务控制机制。

    (3)    命令(Command)对象。数据使用者利用命令对象执行各种数据操作,如查询命令、修改命令等。

    (4)    行集(Rowset)对象。提供了数据的抽象表示,它可以是命令执行的结果,也可以直接由会话对象产生,它是应用程序主要的操作对象。

    OLE DB的对象模型非常简单,这种简单性也带来了灵活性,从上面的几个COM对象也可以看出这一点。下面我们将从应用层角度出发,通过建立在OLE DB基础上的ADO对象模型结构的分析和使用以帮助读者进一步理解一致数据访问技术。

    二、ADO对象模型

    ADO对象模型定义了一组可编程的自动化对象,可用于Visual BasicVisual C++Java以及其它各种支持自动化特性的脚本语言。ADO最早被用于Microsoft Internet Information Server中访问数据库的接口,与一般的数据库接口相比,ADO可更好地用于网络环境,通过优化技术,它尽可能地降低网络流量;ADO的另一个特性是使用简单,不仅因为它是一个面向高级用户的数据库接口,更因为它使用了一组简化的接口用以处理各种数据源。这两个特性使得ADO必将取代RDODAO,成为最终的应用层数据接口标准。

    从图1我们也看到了ADO实际上是OLE DB的应用层接口,这种结构也为一致的数据访问接口提供了很好的扩展性,而不再局限于特定的数据源,因此,ADO可以处理各种OLE DB支持的数据源。

    下面我们先给出ADO的对象模型图:

    2 ADO对象模型

    ADO模型中,主体对象只有三个:ConnectionCommandRecordset,其它四个集合对象ErrorsPropertiesParametersFields分别对应ErrorPropertyParameterField对象,整个ADO对象模型由这些对象组成。

    一个典型的ADO应用使用Connection对象建立与数据源的连接,然后用一个Command对象给出对数据库操作的命令,比如查询或者更新数据等,而Recordset用于对结果集数据进行维护或者浏览等操作。Command命令所使用的命令语言与底层所对应的OLE DB数据源有关,不同的数据源可以使用不同的命令语言,对于关系型数据库,通常使用SQL作为命令语言。

    ConnectionCommandRecordset三个对象中,Command对象是个可选对象,它是否有效取决于OLE DB数据提供者是否实现了ICommand接口。由于OLE DB可提供关系型数据源也可以提供非关系型数据源,所以在非关系型数据源上使用传统的SQL命令查询数据有可能无效,甚至Command命令对象也不能使用。

    从结构上看,ADO模型非常简单,但使用上又非常灵活,下面我们先从单个对象的角度进行讨论:

    (1)    Connection对象。Connection对象代表与数据源之间的一个连接,ADOConnection对象封装了OLE DB的数据源对象和会话对象。根据OLE DB提供者的不同性能,Connection对象的特性也有所不同,所以Connection对象的方法和属性不一定都可以使用。利用Connection对象,我们可以完成以下一些基本设置操作:

    a.    通过ConnectionStringConnectionTimeOutMode属性设置连接串、超时信息、访问模式。

    b.    还可以设置CursorLocation属性以便指定使用客户端游标,以便在客户程序中使用批处理修改方式。

    c.    设置连接的缺省数据库属性DefaultDatabase

    d.    设置OLE DB提供者的属性Provider

    e.    通过OpenClose控制Connection对象与物理数据源的连接。

    f.     通过Execute方法执行命令。

    g.    提供事务机制,通过BeginTransCommitTransRollbackTrans方法实现事务控制。

    h.    通过Errors集合属性检查数据源的错误信息。

    i.      通过OpenSchema方法获取数据库的表信息。

    Connection对象是ADO的基本对象之一,它独立于所有其它的对象。如果我们要对数据库进行查询操作,既可以使用Execute方法,也可以使用Command对象。使用Execute方法比较简便,但用Command对象可以保存命令的信息,以便多次查询。

    (2)    Command对象。Command对象代表一个命令,可以通过其方法执行针对数据源的有关操作,比如查询、修改等。Command对象的用法如下:

    a.    通过CommandText属性设置命令串。

    b.    通过Parameters集合属性和Parameter对象定义参数化查询或存储过程的参数。

    c.    通过Execute方法执行命令,可能的话,返回Recordset对象。

    d.    在执行命令之前,可通过设置CommandType属性以便优化性能。

    e.    可以通过Prepared属性指示底层的提供者为当前命令准备一个编译过的版本,以后再执行时,速度会大大加快。

    f.     通过CommandTimeOut属性设置命令执行的超时值(以秒为单位)

    g.    可以设置ActiveConnection属性,为命令指定连接串,Command对象将在内部创建Connection对象。

    h.    可以设置Name属性,这样以后可以在相应的Connection对象上按Name属性指定的方法名执行。

    Command对象执行时,既可以通过ActiveConnection属性指定相连的Connection对象,也可以独立于Connection对象,直接指定连接串,即使连接串与Connection对象的连接串相同,Command对象仍然使用其内部的数据源连接。

    (3)    Recordset对象。Recordset对象代表一个表的记录集或者命令执行的结果,在记录集中,总是有一个当前的记录。记录集是ADO管理数据的基本对象,所有的Recordset对象都按照行列方式的表状结构进行管理,每一行对应一个记录(Record),每一列对应一个域(Field)Recordset对象也通过游标对记录进行访问,在ADO中,游标分四种:

    a.    静态游标。提供对数据集的一个静态拷贝,允许各种移动操作,包括前移、后移等等,但其他用户所做的操作反映不出来。

    b.   动态游标。允许各种移动操作,包括前移、后移等等,并且其他用户所做的操作也可以直接反映出来。

    c.   前向游标。允许各种前向移动操作,不能向后移动,并且其他用户所做的操作也可以直接反映出来。

    d.   键集(keyset)游标。类似于动态游标,也能够看到其他用户所作的数据修改,但不能看到其它用户新加的记录,也不能访问其他用户删除的记录。

    Recordset对象的用法如下:

    a.    利用CursorType属性设置游标类型。

    b.   通过Open方法打开记录集数据,既可以在Open之前对ActiveConnection属性赋值,指定Recordset对象使用连接对象,也可以直接在Open方法中指定连接串参数,则ADO将创建一个内部连接,即使连接串与外部的连接对象相同,它也使用新的连接对象。

    c.   Recordset对象刚打开时,当前记录被定位在首条记录,并且BOFEOF标志属性为FALSE,如果当前记录集为空记录集,则BOFEOF标志属性为TRUE

    d.   通过MoveFirstMoveLastMoveNextMovePrevious方法可以对记录集的游标进行移动操作。如果OLE DB提供者支持相关的功能的话,可以使用AbsolutePositionAbsolutePageFilter属性对当前记录重新定位。

    e.    ADO提供了两种记录修改方式:立即修改和批修改。在立即修改方式下,一旦调用Update方法,则所有对数据的修改立即被写到底层的数据源。在批修改方式下,可以对多条记录进行修改,然后调用UpdateBatch方法把所有的修改递交到底层数据源。递交之后,可以用Status属性检查数据冲突。

    Recordset对象是ADO数据操作的核心,它既可以作为Connection对象或Command对象执行特定方法的结果数据集,也可以独立于这两个对象而使用,由此可以看出ADO对象在使用上的灵活性。

    上面三个对象都包含一个Property对象集合的属性,通过Property对象可使ADO动态暴露出底层OLE DB提供者的性能。由于并不是所有的底层提供者都有同样的性能,所以ADO允许用户动态访问底层提供者的能力。这样既使得ADO很灵活,又提供了很好的扩展性。

    ADO的其它集合对象及其元素对象,都用在特定的上下文环境中,比如,Parameter对象一定要与某个Command对象相联系后,才能真正起作用。而另外三个对象FieldErrorProperty对象只能依附于其父对象,不能单独创建这些对象。

    在多种语言中使用ADO

    以上介绍了ADO的对象模型,现在我们来讨论如何在不同的语言环境中使用ADO对象。因为ADO是作为自动化组件程序实现的,所以我们可以在任何支持COM和自动化特性的语言环境中使用ADO,比如Visual BasicVisual C++ASP(Active Script Page)Java等等,下面分别加以介绍。

    (1) Visual Basic应用中使用ADO

    Visual Basic应用(简称为VBA)在设计模式和运行模式下都可以创建和使用自动化对象,在设计模式下,象ADO这样的对象库可以作为内部对象来使用,我们只需在“Project”菜单下的“References”命令弹出的对话框中选中ADO对象库“Microsoft ActiveX Data Objects Library”,于是我们就可以在程序中直接声明或新建ADO对象,举例如下:

    Dim cn as New ADODB.Connection

    Dim cmd as New ADODB.Command

    Dim rs as New ADODB.Recordset

    可以看出,在设计时刻使用ADO对象非常方便,而且Visual Basic设计环境中提供的对象浏览器(Object Browser)功能允许用户很方便地查看ADO对象的属性和方法。

    我们也可以在运行时刻创建自动化对象,使用Visual BasicCreateObject函数可以创建任意的自动化对象,由于ADO中只有Connection对象、Command对象和Recordset对象可以被独立创建,所以我们也只能创建这三种对象,举例如下:

    Dim cn

    Set rs=CreateObject("ADODB.Connection ")

    Dim cmd

    Set rs=CreateObject("ADODB.Command")

    Dim rs

    Set rs=CreateObject("ADODB.Recordset")

    不管是设计模式还是运行模式,调用ADO对象的属性和方法都非常简单,直接调用即可。

    (2) Active Server PageVBScript 中使用ADO

    ADO对象也可以用于HTMLActive Server PageVBScript脚本代码,VBScript脚本代码与Visual Basic的代码很类似,它们内嵌在HTMLASP文件的特定标记对内部。但VBScript引擎比Visual Basic的设计环境或运行库在功能上还是要弱一些,首先,在VBScript代码中,没有与设计环境类似的用法,VBScript引擎不能装入ADO类型库,所以不能使用New操作符创建ADO对象,但可以使用CreateObject函数创建对象;其次,ADO对象库中用到的常量只能通过包含文件引入,随ADO一起提供的Adovbs.inc文件包含所有ADO常量的定义,我们可在脚本代码中直接包含此文件。

    因此,为了在VBScript代码中使用ADO,首先要包含Adovbs.inc文件,然后使用CreateObject函数创建ADO对象,以后就可以调用这些对象的属性或方法了。下面的例子显示了在ASP文件中用ADO列出数据表中所有作者的姓名和职称,代码如下:

    <%@ LANGUAGE = VBScript %>

    <HTML>

    <TITLE>Using ADO in a Visual Basic Script Web Page</TITLE>

    </HEAD>

    <LANGUAGE="VBS">

    <!--#include file="adovbs.inc"-->

    <CENTER>

    <H1><font size=4>Using ADO in a Visual Basic Script Web Page</H1></font><br><br>

    <%set myConnection = CreateObject("ADODB.Connection")

       myConnection.Open "DSN=MySamples;UID=sa"

       SQLQuery = "select AuthorName, Title from AuthorDB"

       set rs = myConnection.Execute(SQLQuery)%>

    <TABLE align=center COLSPAN=8 CELLPADDING=5 BORDER=0 WIDTH=200>

    <!-- BEGIN column header row -->

    <TR>

       <TD  VALIGN=TOP BGCOLOR="#800000">

    <FONT STYLE="ARIAL NARROW" COLOR="#ffffff" SIZE=1>

          Title ID</FONT>

       </TD>

       <TD ALIGN=CENTER BGCOLOR="#800000">

    <FONT STYLE="ARIAL NARROW" COLOR="#ffffff" SIZE=1>

          Title</FONT>

       </TD>

       </TR>

    <!-- Get Data --><% do while not rs.EOF %>

       <TR>

       <TD BGcolor ="f7efde" align=center><font style ="arial narrow" size=1>

          <%=rs("AuthorName")%></font>

       </TD>

       <TD BGcolor ="f7efde" align=center><font style ="arial narrow" size=1>

          <%=rs("Title") %>   </font>

       </TD>

       </TR>

    <% rs.MoveNext%>

    <%loop %>

    <!-- Next Row -->

    </TABLE>

    </center>

    </BODY>

    </HTML>

    (3) Visual C++中使用ADO

    Visual C++中使用ADO有多种方法,第一种方法,我们使用CoCreateInstance函数创建ADO对象,并得到对象的IDispatch接口指针,然后调用其Invoke函数,用这种方法需要我们自己处理参数和返回值,ADO提供了Adoid.hAdoint.h头文件分别定义了ADO对象的CLSID和接口ID;第二种方法,利用#import编译指示符(Visual C++ 5.0及以后的版本中可以使用),可以方便地使用ADO对象;第三种方法是利用MFC(Microsoft Foundation Class)库提供的IDispatch接口封装类COleDispatchDriver创建和调用ADO对象。

    下面的代码显示了在Visual C++创建数据源连接的过程:

             GUID connectionCLSID;

             HRESULT hResult = ::CLSIDFromProgID(L"ADODB.Connection", &connectionCLSID);

             if (FAILED(hResult))

             {

                       ……

    return;

             }

             IDispatch *pDispatch = NULL;

             hResult = CoCreateInstance(connectionCLSID, NULL,

                       CLSCTX_SERVER, IID_IDispatch, (void **)&pDispatch);

             if (FAILED(hResult))

             {

                       ……

    return;

             }

             COleDispatchDriver driver;

             driver.AttachDispatch(pDispatch, FALSE);

             TRY {

                       BYTE parms[] =

                                VTS_BSTR;

                       driver.InvokeHelper(0xa, DISPATCH_METHOD, VT_EMPTY,

    &hResult, parms,

                                         L"Provider=SQLOLEDB;User ID=sa;Password=;"

                                         L"Initial Catalog=LEAVES;Data Source=NetTestServer");

             } END_TRY

             driver.DetachDispatch();

    ……

    (4) Java中使用ADO

    Java程序中可以引入ADODB类,然后声明ADO变量,也可以使用new操作符创建ADO变量。下面的代码说明了如何在Java中打开与数据源连接:

    import msado10.*;

    _Connection  m_conn = null;     

    _Recordset   m_rs = null;

    _Command   m_cmd = null;

    void OpenConnection()

    {

    String       s;

    Properties   properties;

         

    try

    {  

    properties = m_conn.getProperties();

    m_conn.Open("dsn=MySamples", "sa", "");

    properties = null;

    }

    catch (Exception e)

    {  

    System.out.println("/nUnable to make a connection /n");

    }

    }

    、ADO高级特性

    为了更全面地介绍一致数据访问技术,这一部分将重点介绍ADO的一些高级特性。我们在编写数据访问应用系统时,通常情况下只是简单地对数据进行各种增、删、改的操作,但有时情形要复杂一些,我们可能需要考虑性能,也可能需要支持大数据量,或者支持长数据类型等等,在这些情况下,我们可以考虑使用ADO的一些高级特性。下面分别介绍这些高级特性。

    (1) 处理数据定义语言

    数据定义语言是指支持数据库对象维护的一些SQL语句,比如CREATE TABLEDROP TABLEALTER TABLE等。因为执行这些语句并不产生记录集,所以我们没有必要使用Recordset对象,只需使用Command对象即可。为了区别于存储过程(store procedure)或用于数据查询的数据定义语句,Command对象的CommandType属性必须指定为adCmdText。下面的代码显示了这种用法:

    Dim Cn As New ADODB.Connection

    Dim Cmd As New ADODB.Command

    ' If the ADOTestTable does not exist

    On Error GoTo AdoError

    Cn.Open "MySamples", "sa"

    Set Cmd.ActiveConnection = Cn

    Cmd.CommandText = "drop table MyTestTable"

    Cmd.CommandType = adCmdText

    Cmd.Execute

    Cmd.CommandText = "set nocount on"

    Cmd.Execute

    Cmd.CommandText = "create table MyTestTable (id int, Name char(100))"

    Cmd.Execute

    Cmd.CommandText = "insert into MyTestTable values(1, 'Pan Aimin')"

    Cmd.Execute

    Cn.Close

    Exit Sub

    AdoError:

          ' handle error ......

    (2) 使用“编译过”的Command对象

    在第二部分介绍Command命令对象时,我们曾经说过可以通过Prepared属性指示底层的提供者为当前查询命令准备一个编译过的版本,以后再执行此命令时,速度会大大加快。如果我们在执行命令之前,把Command对象的Prepared属性设置为TRUE,则命令被首次执行时,其查询串被优化处理,以后再调用此查询命令时,性能会明显提高,因为查询串已经被优化和“编译”过了。而且Prepared属性对于参数化的查询串也有效。

    当然并不是所有的OLE DB提供者都支持这种特性,SQL Server OLE DB支持这种特性,当设置了Prepared属性为TRUE的命令被执行时,底层的提供者实际上创建了一个临时的存储过程,当下次执行此命令时,提供者启动存储过程。当命令对象或连接对象被删除时,此临时存储过程也被删除。

    如果Command对象只被执行一次,那么使用Prepared属性没有任何意义,甚至反而降低效率。

    Prepared属性的用法比较简单,这里不再给出代码。

    (3) 存储过程和参数处理

    存储过程与前面介绍的“编译过”的命令对象有点类似,存储过程运行在服务器上,它可以把复杂的应用逻辑封装在服务器一端,而在客户程序中只需调用简单的SQL语句即可。如果我们要在ADO的Command对象中执行存储过程,可以把CommandType属性指定为adCmdStoredProc,当然,在存储过程类型的Command对象中,不要再指定Prepared属性为TRUE。存储过程的用法非常简单,下面是一个例子:

    Dim Cmd As New ADODB.Command

    Dim rs As New ADODB.Recordset

    Cmd.ActiveConnection = "DSN=MySamples;uid=sa"

    Cmd.CommandText = "MyProc"

    Cmd.CommandType = adCmdStoredProc

    Set rs = Cmd.Execute()

    Debug.Print rs(0)

    rs.Close

    存储过程允许包含输入输出参数和返回值,对应到Command对象中,这些参数就是Parameter对象,输入参数的处理比较简单,输出参数和返回值的处理有所不同,只有当返回的Recordset对象的记录全部遍历或Recordset对象关闭之后,输出参数和返回值才真正有效。

    对于下面的存储过程:

    CREATE PROCEDURE MyProc @ioparm int OUTPUT AS

    SELECT name FROM MyTestTable WHERE id < 2

    SELECT @ioparm = 1

    RETURN 100

    下面的代码执行此存储过程:

    Dim Cmd As New ADODB.Command

    Dim rs As New ADODB.Recordset

    Dim param As Parameter

       

    Cmd.ActiveConnection = "DSN=MySamples;UID=sa"

    Cmd.CommandText = "MyProc"

    Cmd.CommandType = adCmdStoredProc

    ' Set up parameters.

    Set param = Cmd.CreateParameter("Return", adInteger, adParamReturnValue, , 0)

    Cmd.Parameters.Append param

    Set param = Cmd.CreateParameter("Output", adInteger, adParamOutput, , 0)

    Cmd.Parameters.Append param

    Set rs = Cmd.Execute

    If Not rs.EOF And Not rs.BOF Then

    Debug.Print rs(0)     

    rs.Close

    End If

    Debug.Print Cmd(0) ' The return code

    Debug.Print Cmd(1) ' The Output parameter
     

    (4) 批修改处理

    我们知道Recordset对象的Update方法用于修改当前记录,而UpdateBatch方法则用于递交所有对当前记录集的增、删、改操作。把Recordset对象的LockType属性设置为adLockBatchOptimistic,则UpdateBatch方法有效,当然,不同的OLE DB提供者可能还会有不同的要求,比如,SQL Server提供者也要求游标类型为键集游标或静态游标。UpdateBatch方法可以一次把客户端所有的修改传送到数据库中,相对应地,也可以调用CancelBatch方法取消所有的修改操作。

    在调用了UpdateBatch方法之后,错误处理与通常有所不同。在错误处理过程中,可以通过设置Recordset对象的Filter属性为adFilterConflictingRecords,然后对冲突的记录逐个进行处理。

    下面我们给出一个批修改处理的例子:

    Dim rs As New ADODB.Recordset

    rs.CursorLocation = adUseClient

    rs.CursorType = adOpenKeyset

    rs.LockType = adLockBatchOptimistic

    rs.Open "select * from MyTestTable", "DSN=MySamples;uid=sa"

    ' Change the type for a specified title.

    While (Not rs.EOF)

    If Trim(rs("Title")) = " instructor" Then

           rs("Title") = "Engineer"

    End If

    rs.MoveNext

    Wend

    rs.UpdateBatch

    rs.Close

    (5) 多记录集处理

    有些情况下,一个SQL语句可以产生多个记录集,存储过程也可以产生多个记录集。在这些情况下,我们可以逐个获取记录集,利用Recordset对象的NextRecordset方法可以依次获得每一个记录集,如果所有的记录集都已经获得,则最后调用NextRecordset方法返回Nothing。

    下面的代码说明了这种用法:

    Dim cmd As New ADODB.Command

    Dim rs As ADODB.Recordset   

    Cmd.ActiveConnection = "DSN=MySamples;UID=sa"

    Cmd.CommandText = "MyNextProc"

    Cmd.CommandType = adCmdStoredProc

    Set rs = Cmd.Execute()

    While Not rs Is Nothing

        If (Not rs.EOF) Then

            Debug.Print rs(0)

        End If

        Set rs = rs.NextRecordset()

    Wend

    (6) 客户端游标和服务器端游标

    游标服务是数据访问的重要内容,在第二部分介绍Recordset对象时,我们说明了游标的四种类型,在Recordset对象还有一个属性CursorLocation用于指定游标的位置,我们可以指定使用客户端的游标,也可以指定使用服务器端游标。CursorLocation属性的缺省值为adUseServer,使用服务器端游标的好处是,程序对数据库的修改可以立即反映到服务器,而且,其他用户对数据库的操作也可以马上反映出来,但使用服务器端游标带来了高网络流量,每一个数据访问都需要通过网络交换数据。

    ADO提供了客户端数据缓存处理,因此,在打开Recordset对象前,可以设置CursorLocation为adUseClient,指定使用客户端游标。通过客户端游标,ADO利用本地数据缓存以降低网络流量,虽然在数据访问灵活性上有所损失,但却大大提高了通过网络访问数据库的性能。

    (7) 长数据类型处理

    现在的应用越来越离不开数据库的长数据类型,长数据类型包括文本或二进制信息,可用于记录图像、声音及其它多媒体或非多媒体信息。有时候长数据并不很长,可以直接通过Field对象的Value属性进行读取或赋值;有时候长数据非常长,长到内存中难以放下,这时可以通过Field对象的块操作进行访问。

    用Field对象的GetChunk方法可以读取部分或全部的数据,在内存使用有限制的情况下,就可以用GetChunk读取部分数据,连续调用GetChunk以读取所有的数据。对应地,我们可以连续调用Field对象的AppandChunk方法对当前记录的某个长数据类型域赋值。请注意,GetChunk和AppandChunk只对Attributes属性为adFldLong的Field对象有效。

    有时候,在Command对象的Parameters集合的Parameter对象中也需要使用长数据类型,所以GetChunk和AppandChunk方法对于Parameter对象同样适用。

    下面给出一个使用GetChunk和AppandChunk的例子:

    Dim Cn As New ADODB.Connection

    Dim rsRead As New ADODB.Recordset

    Dim rsWrite As New ADODB.Recordset

    Dim strChunk As String

    Dim Offset As Long

    Dim Totalsize As Long

    Dim ChunkSize As Long

    Cn.Open "MySamples", "sa"

    rsRead.CursorType = adOpenStatic

    rsRead.Open "select My_LongData from MyTestTable", Cn

    rsWrite.CursorType = adOpenKeyset

    rsWrite.LockType = adLockBatchOptimistic

    rsWrite.Open "select * from MyBLOB", Cn

    ChunkSize = 1000

    Totalsize = rsRead("My_LongData").ActualSize

    Do while Offset < Totalsize

        strChunk = rsRead("My_LongData").GetChunk(ChunkSize)

        Offset = Offset + ChunkSize

        rsWrite("Info").AppendChunk strChunk

    Loop

    rsWrite.UpdateBatch

    rsWrite.Close

    rsRead.Close

    (8) Recordset对象的永久存储机制

    Recordset对象的永久存储机制是ADO 2.0版本新增的功能,我们可以把Recordset对象保存到一个文件中,以后再从文件中打开Recordset对象,并继续操作。当我们在使用客户端游标时,可以使用该功能,首先连接到数据源,并执行查询,然后调用Recordset对象的Save方法,把Recordset对象保存到文件中。即使关闭了机器,当以后再打开机器时,执行Open("filename", , ,adCmdFile),可以继续执行数据操作。

    (9) 异步操作和事件响应机制

    ADO 2.0版本支持异步操作,当连接数据源或进行记录集访问时,我们可以选择异步方式。使用异步方式进行数据源连接时,调用Open后,函数马上返回,在等待连接完成之前,我们可以继续执行其它的操作。当异步操作完成后,我们可以接到操作完成的通知。Recordset对象在批量获取数据时也支持异步操作(要求使用客户端游标),它只返回查询结果的第一条记录,以后可以在后台继续获取后面的记录。

    与异步操作相对应的是事件机制,Connection和Recordset对象提供了很多通知事件,包括连接完成、命令将执行、事务处理各阶段、记录集在进行数据修改或其它一些操作等等,ADO都提供了事件通知。通过事件控制函数,我们可以编写出更加规范的客户程序,提供更为灵活的前后台处理方式。

    五、使用ADO访问数据

    前面我们已经介绍了ADO的结构和特性,也简单介绍了在不同语言中调用ADO对象的方法,现在我们从开发应用的角度来进一步讲述如何利用ADO进行数据访问。

    (1) 如何建立数据源连接

    建立连接的核心是使用连接串。连接串包含所有用来建立数据源连接的信息,它使用一系列格式为“参数=值”的语句,并用“;”连接起来。ADO根据连接串建立连接,首先它找到“Provider”参数信息,以便调用适当的OLE DB提供者。ADO可以理解以下四个参数信息:

    Provider =

    File Name =

    Remote Provider=

    Remote Server=

    ADO把其它的参数信息传给提供者,由提供者根据这些参数信息建立数据源连接。

    Connection对象既可以使用ConnectionString属性建立连接,也可以在Open方法的参数中指定连接串,甚至可以把二者结合起来,如果在Open的参数和ConnectionString属性中同时出现同一个参数信息,则以Open参数信息为准。我们还可以通过Connection对象的Provider属性指定OLE DB提供者,下面是目前可以使用的一些“Provider”信息:

    MSDASQL —— 针对ODBC的OLE DB提供者;

    SQLOLEDB —— 针对SQL Server的OLE DB提供者;

    ADsDSOObject —— 针对SQL Server的OLE DB提供者;

    MSIDXS —— 针对Microsoft Index Server的OLE DB提供者;

    Microsoft.Jet.OLEDB.3.51 —— 针对Microsoft Jet的OLE DB提供者;

    MSDAORA —— 针对Qracle的OLE DB提供者。

    如果在Open方法参数、ConnectionString或Provider属性中都没有指定OLE DB提供者,则ADO使用缺省的针对ODBC的OLE DB提供者即“MSDASQL”。下面给出三个典型的连接串例子:

    方法一:

    Dim Cn As New ADODB.Connection

    Cn.Open  " Provider=SQLOLEDB;User ID=sa;Password=;" & _

                           "Initial Catalog=MyDataBase;Data Source=MyServer"

    方法二:

    Dim Cn As New ADODB.Connection

    Cn.ConnectionString = " Provider=SQLOLEDB;User ID=sa;Password=;" & _

                           "Initial Catalog=MyDataBase;Data Source=MyServer"

    Cn.Open

    方法三:

    Dim Cn As New ADODB.Connection

    Cn.Provider = "SQLOLEDB"

    Cn.Properties("User ID”).Value = "sa"

             Cn.Properties("Initial Catalog").Value = " MyDataBase"

             Cn.Properties("Data Source").Value = "MyServer"

    Cn.Open

     (2) 如何处理事件

    前面曾经说过,使用ADO 2.0,我们可以在程序中处理Connection对象或者Recordset对象的事件,那么如何在程序中实现呢?利用Visual Basic语言提供的WithEvents关键字可以很方便地建立事件源对象和事件控制函数的连接关系。下面对此作一简单说明。

    首先,为了处理事件源所引发的事件,用 WithEvents 关键字声明对象类变量,例如:

    Option Explicit

    Dim WithEvents Cn As Connection

    然后给出事件控制函数,按照Visual Basic语言约定,事件控制函数的函数名为“事件源名_事件名 ”,例如,我们要响应Connection对象Cn的ConnectComplete事件,则事件控制函数名为Cn_ConnectComplete,代码可以这样来写:

    Private Sub Cn_ConnectComplete(ByVal pError As ADODB.Error,  _

    adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)

    ……

    End Sub

    以后当建立连接Cn完成后,ADO激发ConnectionComplete事件,它自动调用Cn_ConnectComplete函数。

    (3) 执行查询的方法

    利用ADO执行查询的方法很多,最基本的方法是使用Command对象,也可以直接调用Connection对象的Execute方法。下面介绍使用Command对象执行查询的方法。

    首先建立数据源连接Connection对象,然后创建Command对象,

    Dim cmd as New ADODB.Command

    cmd.CommandText = " Select * from AuthorTable"

    cmd.Name = "MyCommand"

    cmd.ActiveConnection = Cn

    然后调用Command对象的Execute方法

    set rs = cmd.Execute

    或者通过Connection对象的方法:

    set rs = cn.MyCommand

    我们也可以不用Command对象,而是直接使用Recordset对象,举例如下:

    Dim rs as New ADODB.Recordset

             rs.CursorType = adOpenKeyset

             rs.LockType = adLockOptimistic

             rs.Open " AuthorTable ", cn, , , adCmdTableset

    甚至连Connection对象都可以不使用,直接由Recordset使用内部连接,只需在Open语句中给出必要的连接信息即可。

    (4) 对记录集数据进行操作

    对记录集数据进行各种移动或修改操作是应用程序访问数据最重要的任务,与其它一些数据访问接口一样,这些操作集中在记录集对象和域对象的属性和方法调用上,在ADO对象模型中,主要是RecordsetField对象的一些方法的用法。

    增加记录操作:AddNew方法,再调用Update方法,如:

            

    rs.AddNew

             rs!Author_id = strID

             rs!AuthorName = strName

             rs!Title = strTitle

             rs.Update

    删除记录操作:调用Delete方法可以删除当前记录或者满足条件的一组记录。在立即修改模式下,删除马上生效;在批修改模式下,Delete操作只在当前缓冲区中的记录上作了标记,只有当调用了UpdateBatch之后,记录才真正被删除。一个简单的例子如下:

             rs.Filter = "Title = 'Engineer'"

    rs.Delete adAffectGroup       ' Delete the record.      

             rs.UpdateBatch

    修改记录操作:对当前记录的各个域信息进行重新赋值后,调用Update方法,如果要取消修改,则调用CancelUpdate方法。举例如下:

    rs!Author_id = strID

             rs!AuthorName = strName

             rs!Title = strTitle

             rs.Update

    移动和遍历操作:Recordset提供了各种方向的移动操作,利用这些操作可以对记录集进行遍历处理。各种移动操作如下表所示:

    操作

    类型

    说明

    MoveFirst

    方法

    移动到记录集首

    MoveLast

    方法

    移动到记录集尾

    MoveNext

    方法

    移动到下一条记录

    MovePrevious

    方法

    移动到上一条记录

    Move

    方法

    往前或往后移动数条记录

    Bookmark

    属性

    返回标识当前记录的书签或设置到指定的书签位置

    BOF

    属性

    若当前位置在首条记录之前,则返回True否则为False

    EOF

    属性

    若当前位置在最后记录之后,则返回True否则为False

    利用这些属性和方法,可以很灵活地访问记录集,下面举例说明:

    rs.MoveFirst

             Do While True

                       ' Display information about current record and get user input.

                       strMessage = "Author: " & rs!AuthorName & _

                                vbCr & "(record " & rs.AbsolutePosition & _

                                " of " & rs.RecordCount & ")" & vbCr & vbCr & _

                                "Please enter command:" & vbCr & _

                                "[1 - next / 2 - previous /" & vbCr & _

                                "3 - set bookmark / 4 - go to bookmark]"

                       intCommand = Val(InputBox(strMessage))

                       Select Case intCommand

                                ' Move forward or backward, trapping for BOF or EOF.

                                Case 1

                                         rs.MoveNext

                                         If rs.EOF Then

                                                   MsgBox "Moving past the last record." & _

                                                            vbCr & "Try again."

                                                   rs.MoveLast

                                         End If

                                Case 2

                                         rs.MovePrevious

                                         If rs.BOF Then

                                                   MsgBox "Moving past the first record." & _

                                                            vbCr & "Try again."

                                                   rstPublishers.MoveFirst

                                         End If

                                ' Store the bookmark of the current record.

                                Case 3

                                         varBookmark = rs.Bookmark

                                ' Go to the record indicated by the stored bookmark.

                       Case 4

                                         If IsEmpty(varBookmark) Then

                                                   MsgBox "No Bookmark set!"

                                         Else

                                                   rs.Bookmark = varBookmark

                                         End If

                                Case Else

                                         Exit Do

                       End Select 

    Loop

    示例程序

    至此我们已经全面介绍了Microsoft一致数据访问技术,重点讲述了ADO对象模型以及它的用法,最后我们通过一个简单的例子来说明用ADO访问数据库的基本用法。

    例程序在Visual Basic环境下完成,首先创建一个标准的EXE工程,使用工程的缺省表单浏览数据库。我们把工程名改为ADOSample,把表单名改为ADOSampleForm。程序最终的运行结果如图3所示。

    3 例程序运行界面

    为了在工程中使用ADO,首先我们使用“Project”菜单下的“References”命令,选中“Microsoft ActiveX Data Objects Library”,以后我们在程序中就可以直接使用ADO的对象了。

    接下去我们使用“Project”菜单下的“Components”命令选中“Microsoft FlexGrid Control”控制,然后在表单中加入FlexGrid控制,我们使用缺省的控制名MSFlexGrid1

    为了简化程序,我们使用了Visual Studio 6.0随带的Access数据库文件Nwind.mdb,在程序中加入连接串定义,所有的全局变量定义如下:

    Dim WithEvents cn As ADODB.Connection

    Dim cmd As ADODB.Command

    Dim rs As ADODB.Recordset

    Dim ConnectString As String

    当程序启动时,建立连接对象,代码如下:

    Private Sub Form_Load()

        Set cn = New ADODB.Connection

        On Error GoTo EH

       

        Const ConnectString = "Provider=Microsoft.Jet.OLEDB.3.51;" & _

                    "Data Source=c:/Program Files/Microsoft Visual Studio/vb98/Nwind.mdb"

        cn.ConnectionTimeout = 5

        cn.CursorLocation = adUseClient

        cn.Open ConnectString, Options:=adAsyncConnect

        Exit Sub

      EH:

        Select Case Err

            Case 40002:

                MsgBox "Could not connect... " & Error

            Case Else

                Debug.Print Err, Error

        End Select

    End Sub

    当连接完成后,我们执行一个基本的查询命令,并调用ConfigFlexGrid函数配置FlexGrid控制的显示格式,调用ShowDataInFlexGrid函数显示结果记录集,下面是连接对象的事件控制函数:

    Private Sub Cn_ConnectComplete(ByVal pError As ADODB.Error, _

        adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)

        Set cmd = New ADODB.Command

        cmd.CommandText = "Customers"

        cmd.CommandType = adCmdTable

        cmd.ActiveConnection = cn

       

        Set rs = cmd.Execute

        MSFlexGrid1.Visible = False

        ConfigFlexGrid

        ShowDataInFlexGrid

        MSFlexGrid1.Visible = True

    End Sub

    下面是ConfigFlexGridShowDataInFlexGrid函数的代码:

    Private Sub ConfigFlexGrid()

        MSFlexGrid1.Left = 0

        MSFlexGrid1.Top = 0

        MSFlexGrid1.Rows = 1

        MSFlexGrid1.Cols = 8

        MSFlexGrid1.Row = 0

        MSFlexGrid1.Col = 1

        MSFlexGrid1.ColWidth(0) = 300

        MSFlexGrid1.ColWidth(1) = 1500

        MSFlexGrid1.ColWidth(2) = 2500

        MSFlexGrid1.ColWidth(3) = 1500

        MSFlexGrid1.ColWidth(4) = 1500

        MSFlexGrid1.ColWidth(5) = 1500

        MSFlexGrid1.ColWidth(6) = 1200

        MSFlexGrid1.ColWidth(7) = 1200

        MSFlexGrid1.TextMatrix(0, 1) = "Customers ID"

        MSFlexGrid1.TextMatrix(0, 2) = "Company Name"

        MSFlexGrid1.TextMatrix(0, 3) = "Contact Name"

        MSFlexGrid1.TextMatrix(0, 4) = "Contact Title"

        MSFlexGrid1.TextMatrix(0, 5) = "City"

        MSFlexGrid1.TextMatrix(0, 6) = "Country"

        MSFlexGrid1.TextMatrix(0, 7) = "Phone"

    End Sub

    Private Sub ShowDataInFlexGrid()

        Dim strTemp As String

       

        On Error Resume Next

       

        MSFlexGrid1.Rows = 1

        rs.MoveFirst

        Do While Not rs.EOF

            strTemp = ""

            strTemp = strTemp & vbTab & rs!CustomerID

            strTemp = strTemp & vbTab & rs!CompanyName

            strTemp = strTemp & vbTab & rs!ContactName

            strTemp = strTemp & vbTab & rs!ContactTitle

            strTemp = strTemp & vbTab & rs!City

            strTemp = strTemp & vbTab & rs!Country

            strTemp = strTemp & vbTab & rs!Phone

            MSFlexGrid1.AddItem strTemp

            rs.MoveNext

        Loop

    End Sub

    为了使窗口大小调整时,FlexGrid控制也能够随着变化,响应表单的Resize事件,事件控制函数如下:

    Private Sub Form_Resize()

        MSFlexGrid1.Width = Width - 100

        MSFlexGrid1.Height = Height - 400

    End Sub

    当程序退出时,做基本的清理工作,代码为:

    Private Sub Form_Terminate()

        Set cn = Nothing

        Set cmd = Nothing

        Set rs = Nothing

    End Sub

    现在我们可以运行程序了,读者可以试一试。

    结束语

    随着信息化时代的不断深入,信息处理和信息共享变得越来越重要,数据访问技术对于信息系统也就显得尤为重要,本文介绍了Microsoft提供的一致数据访问(UDA)技术,从基本的结构模型到深入介绍ADO对象模型,并从应用的角度分析了ADO的一些特性,最后通过例程序展示了ADO的基本用法。

    本文旨在对UDA技术作一个基本介绍,希望文中所讲述的内容能帮助读者更好地理解和使用这种技术。

    真的是长篇大论,好好看看在继续了,呵呵……


    后记:


    作者文章合集:http://www.csdn.net/develop/author/pam/index.shtm

  • 相关阅读:
    【Python3之匿名函数及递归】
    【Python3之模块及包的导入】
    :nth-child和:nth-of-type的区别
    JavaScript ES6中export及export default的区别以及import的用法
    vue中npm run dev运行项目不能自动打开浏览器! 以及 webstorm跑vue项目jshint一直提示错误问题的解决方法!
    SEO优化之HTML代码优化最重要的5个标签
    清除浮动小记,兼容Ie6,7
    JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承
    面向对象JS基础讲解,工厂模式、构造函数模式、原型模式、混合模式、动态原型模式
    纯CSS实现垂直居中的几种方法
  • 原文地址:https://www.cnblogs.com/ainima/p/6331602.html
Copyright © 2011-2022 走看看