📄 206003.htm
字号:
<html><body><span id=Layer1><a name=206003><font color=#3e70d7 face=arial size=5><b>ActiveX Data Objects</span><span id=Layer2></b></font><p><font size=2 color=#3c3c3c face=arial>今天,Microsoft一直鼓励程式设计师使用的资料存取介面便是ADO。ADO不过就是一组COM物件,提供架构在OLE DB之上的一层介面。不过和OLE DB不同点是,OLE DB物件通常有许多介面,每个ADO物件则透过一个单一介面来显露所有的method。C++、Visual Basic与Java(Visual J++)都可以存取拥有多重介面的COM物件,但有些简单的语言如 : Microsoft Visual Basic Scripting Edition (VBScript)就无法办到这一点。因此ADO可以在Windows 2000环境下的任一种语言中使用。也因为目前ADO对新应用程式而言已是最流行的资料存取介面,本章接下来便专注於ADO 2.5的详细内容,这个版本的ADO是随着Windows 2000第一次发行时出货的。</span><span id=Layer3></font></p><p><font size=2 color=#3c3c3c face=arial>ADO是以COM为基础,但比OLE DB简单多了</span><span id=Layer4></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>ADO是以COM为基础,但比OLE DB简单多了</span><span id=Layer5></font></p><hr><p><font size=2 color=#3c3c3c face=arial>关於这项技术的第一个重点:在整个讨论的过程中,关於「客户端」这个字眼是指任何一个使用ADO来存取资料来源的软体。客户端的软体可能执行在桌上型机器,但对叁层式的应用程式来说(本书假设的模式),ADO客户端一般指的是执行在中间层服器上的。不要搞混了;以目前的情况而言,ADO客户端不必要出现在客户端机器上。这也就是说,在使用者的桌上型电脑上。</span><span id=Layer6></font></p><p><font size=2 color=#3c3c3c face=arial>ADO客户端可以执行在桌上型电脑,或在中间层伺服器机器上</span><span id=Layer7></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>ADO客户端可以执行在桌上型电脑,或在中间层伺服器机器上</span><span id=Layer8></font></p><hr><font color=#3e72d7 face=arial size=4><b>ADO概览</span><span id=Layer9></b></font><p><font size=2 color=#3c3c3c face=arial>ADO定义叁个主要的COM物件类别:包含Connection、Command与Recordset,以及许多辅助的元件(如Error和Parameters)。将这些物件组织在一起便形成了所谓的ADO物件模型。这叁个物件中最基本的一个便是Connection物件。它的method允许客户端建立一个连线,连结到特定的资料来源(Data Source),如特定的Oracle资料库。因为ADO是实作在OLE DB之上的,客户端需要一个特定的OLE DB provider以存取这个资料来源。</span><span id=Layer10></font></p><p><font size=2 color=#3c3c3c face=arial>ADO 的Connection物件允许你建立一个连线到资料来源</span><span id=Layer11></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>ADO 的Connection物件允许你建立一个连线到资料来源</span><span id=Layer12></font></p><hr><p><font size=2 color=#3c3c3c face=arial>就名称所暗示,一个ADO Command物件能够包含一个指令,这个指令能够编译,最後传送到资料来源。举例来说,若资料来源是一个关联式资料库,则这个指令可能是一个如下的SQL要求:</span><span id=Layer13></font></p><div style="background-color: #D7D7D7;"><font face=Arial size=3><pre>SELECT Salary FROM Employees WHERE Name='Smith '</span><span id=Layer14></pre></font></div><p><font size=2 color=#3c3c3c face=arial>ADO也允许使用其它的选择:Command物件并非一定得使用SQL,不过因为关联式资料库仍是搭配ADO使用最常见的一个资料来源,SQL查询是最常见的选择。如果你愿意的话,你也可以将欲执行的指令指定为资料库中既存的预存程序(stored procedure)。</span><span id=Layer15></font></p><p><font size=2 color=#3c3c3c face=arial>ADO的Command物件内含指令,如一个SQL查询,用来存取资料</span><span id=Layer16></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>ADO的Command物件内含指令,如一个SQL查询,用来存取资料</span><span id=Layer17></font></p><hr><p><font size=2 color=#3c3c3c face=arial>ADO物件模型的第叁个重点是Recordset物件。当一个客户端建立一个连结到特定资料来源的连线後,这个指令可能会回传一个结果。举例来说,刚提及的SQL查询可能会根据查询执行的结果,从关联式资料库回传一组记录(也称为横列)。这个结果以Recordset物件的型式回传到客户端,而这个Recordset的内容可让客户端检视或修改。</span><span id=Layer18></font></p><p><font size=2 color=#3c3c3c face=arial>ADO的Rec-ordset物件可以包含查询回传的结果</span><span id=Layer19></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>ADO的Rec-ordset物件可以包含查询回传的结果</span><span id=Layer20></font></p><hr><p><font size=2 color=#3c3c3c face=arial>Connection、Command与Recordset是ADO定义的叁个最重要的COM类别,除此之外还有许多有趣的类别。这些类别包括:</span><span id=Layer21></font></p><font size=2 color=#3c3c3c face=arial><ul><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> Field</span><span id=Layer22> </b></font>每个Recordset物件包含一笔或多笔记录。每笔记录中包含一或多个值,而客户端所看到的每个值就视为一个Field物件。若要检视或更改某些Recoreset记录中的值,客户端可以存取包含这些值的Field物件。</span><span id=Layer23></li><br></font><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> Record</span><span id=Layer24> </b></font>如同名称一般,一个Record物件可用来代表,并存取Recordset物件中特定的横列。不过Record也可用在一般的地方。举例来说,一群Record物件可以组织成树状结构,然後用来存取阶层式资料,如在Windows 2000档案系统的某个特殊目录之下所有的档案。ADO 2.5新功能中包含Record物件,我们将在本章稍後详细描述。</span><span id=Layer25></li><br></font><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> Stream</span><span id=Layer26> </b></font>Stream物件包含位元组字串。这些位元组可能是档案系统中一个档案的内容,一区块记忆体的内容,或其它东西。Stream和Record物件一样是ADO 2.5的新功能,我们将在本章稍後详细描述。</span><span id=Layer27></li><br></font><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> Parameter</span><span id=Layer28> </b></font>某些发行到资料来源的指令永远都不会改变。有些则可以保留标准格式,允许你取代部份的内容。举例来说,在下面的SQL查询中,允许更改搜寻的名称可能会相当的有用:</span><span id=Layer29></li><br></font><br><font size=2 color=#3c3c3c face=arial><div style="background-color: #D7D7D7;"><font face=Arial size=3><pre>SELECT Salary FROM Employees WHERE Name='Smith '</span><span id=Layer30></pre></font></div></font></ul></font><p><font size=2 color=#3c3c3c face=arial>ADO同样定义许多其它的COM类别</span><span id=Layer31></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>ADO同样定义许多其它的COM类别</span><span id=Layer32></font></p><hr><p><font size=2 color=#3c3c3c face=arial>为了要让这个动作更为简单,一个Command物件可以拥有一个关联到自己的Parameter物件。这个物件的名称可以与SQL查询的一致。只要修改这个参数,ADO客户端可以在许多不同的查询中使用相同的Command物件。</span><span id=Layer33></font></p><font size=2 color=#3c3c3c face=arial><ul><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> Error</span><span id=Layer34> </b></font>若ADO物件在执行的过程中发生错误,则会回传一个或多个Error物件。客户端可以检视这个物件以判断哪里出了问题。</span><span id=Layer35></li><br></font><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> Collection</span><span id=Layer36> </b></font>Collection物件提供一个简单的方式以参考到一组相关的物件。举例来说,一个Connection物件拥有一个Errors集合以便在发生问题时存取已建立的所有Error物件。同样地,一个单独的Recordset上所有的Field物件可透过Recordset的Fields集合来存取,而关联到特定Command物件的所有Parameter物件也可以透过物件的Parameters集合来存取。</span><span id=Layer37></li><br></font></ul></font><p><font size=2 color=#3c3c3c face=arial>有些ADO物件是以一般的COM方式建立的,透过一个客户端呼叫CoCreateInstance或CreateObject,或new,或任何程式语言所使用的方式。举例来说,Connection、Command与Recordset物件都能够以相同的方式建立。不过客户端并不会直接建立所有ADO物件,有些物件是偷偷建立的。举例来说,使用一个Command物件执行一个回传资料的查询,此时将会自动地建立一个Recordset物件。而某些ADO物件,如Error物件,则从来就不会让客户端自行建立。当它需要回报一个错误时,这些物件在必要时便经由其它的ADO物件建立。</span><span id=Layer38></font></p><p><font size=2 color=#3c3c3c face=arial>一个OLE DB Provider可同时支援同步以及非同步的操作,因此ADO客户端就有两种选择。在同步的案例中,客户端发出某些要求,然後等待它执行完成。对於非同步操作,客户端不须苦等它的回应。举例来说,客户端可以利用非同步的方式执行一个指令,然後不必等待指令执行完成便可以做别的事。当要求的作业完成时,便透过事件处理函数的回呼函数通知客户端。换句话说,ADO自动地呼叫客户端提供的函数,通知某些操作已经完成了。这些回呼动作(callback)统称事件,能够在一个操作开始之前便发生了,允许ADO客户端能检查将要使用的参数,更改这些参数,或以某些其它的方式来影响这个操作。</span><span id=Layer39></font></p><p><font size=2 color=#3c3c3c face=arial>当非同步操作完成时,ADO事件可以通知客户端</span><span id=Layer40></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>当非同步操作完成时,ADO事件可以通知客户端</span><span id=Layer41></font></p><hr><p><font size=2 color=#3c3c3c face=arial>本章的目的是给你一个ADO如何运作的概念。为了达到这个目的,接下来的章节将详细描述客户端如何使用ADO定义的这些物件。</span><span id=Layer42></font></p><font color=#3e72d7 face=arial size=4><b>开启一个Connection</span><span id=Layer43></b></font><p><font size=2 color=#3c3c3c face=arial>开启一个连结到资料来源的连线最普遍的方式如图 6-2所示:建立一个Connection 物件,设定它的ConnectionString属性,然後呼叫Open method。ConnectionString的值辨识欲连结的资料来源,以及其它必要的资讯,如资料库中使用者的名称与密码。这个字串可以是一个简单的资料来源名称(Data Source Name,DSN) 如「DSN= mydb」,一个URL,或其它的东西。你也可以直接在Connection的Open method 传递一个连线字串 你用不着分别设定这些属性。</span><span id=Layer44></font></p><p><font size=2 color=#3c3c3c face=arial>Connection物件的Conn-ectionString属性识别欲连结的资料来源</span><span id=Layer45></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>Connection物件的Conn-ectionString属性识别欲连结的资料来源</span><span id=Layer46></font></p><hr><br><center><a target=_new href=imagesh/6-2.gif><img border=0 src='imagesl/6-2.gif'></a></center></span><span id=Layer47><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图 6-2</span><span id=Layer48> </b></font>客户端可以呼叫Connection物件的Open method,建立一个到资料来源的连线</span><span id=Layer49></td></table></font></center><p><font size=2 color=#3c3c3c face=arial>客户端可以设定Connection物件上的其它属性以便控制它的行为。举例来说,ConnectionTimeout属性控制ADO介面在回传一个错误之前等待连线建立的时间长短。同样地,客户端指定到物件的CommandTimeout属性控制ADO介面将在放弃执行并回传错误之前,等待在这个连线上所发行的查询之回应时间的长短。客户端也不需要明确地使用一个Connection物件便开启一个到资料来源的连线。完成这个动作其中之一个方式便是让客户端设定Command物件的ActiveConnection属性成一个连线字串。当指令执行时,ADO仍旧会建立一个Connection物件,不过对ADO客户端来说它是隐藏起来的。或者也可让客户端传递一个连线字串做为Recordset物件Open method的参数,它也会产生相同的行为:建立一个Connection物件,但对ADO客户端来说它是隐藏起来的。这些方案对执行单一指令来说是很有用的,不过因为这两者都会在每回指令执行时,建立一个新Connection物件。因此它们对於在同一个连线上执行多次要求的客户端而言算是很没效率的。</span><span id=Layer50></font></p><p><font size=2 color=#3c3c3c face=arial>客户端也可以使用Command或Recordset物件开启连线</span><span id=Layer51></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>客户端也可以使用Command或Recordset物件开启连线</span><span id=Layer52></font></p><hr><font color=#3e72d7 face=arial size=4><b>执行一个查询</span><span id=Layer53></b></font><p><font size=2 color=#3c3c3c face=arial>不用太讶异,ADO提供许多方法来对资料来源定义并执行一个查询。对於已拥有一个Connection物件的客户端来说,最简单的方法就如同图 6-3。(为了让这个范例更切实际,在此讨论时将假设资料来源是一个关联式资料库,不过要注意的是:ADO 也允许存取其它类型的资料来源。) 客户端呼叫Connection物件的Execute method,将查询以字串型式传入。若这个查询造成一或多笔横列的资料,Execute method将会回传一个介面指标到一个包含回传资料的新建立之Recordset物件。若查询并没有回传资料(或许它是一个SQL UPDATE叙述。),就不会回传Recordset。</span><span id=Layer54></font></p><p><font size=2 color=#3c3c3c face=arial>呼叫Connection 物件Execute method的结果是回传一个Record-set</span><span id=Layer55></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>呼叫Connection 物件Execute method的结果是回传一个Record-set</span><span id=Layer56></font></p><hr><br><center><a target=_new href=imagesh/6-3.gif><img border=0 src='imagesl/6-3.gif'></a></center></span><span id=Layer57><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图 6-3</span><span id=Layer58> </b></font>一个ADO客户端可以使用Connection物件的Execute method来执行一个查询。</span><span id=Layer59></td></table></font></center><p><font size=2 color=#3c3c3c face=arial>如图6-4,另一个执行操作的方法是建立并使用一个Command物件。一旦这个物件存在,客户端便可设定它的ActiveConnection属性,以便包含一个连线字串或一个指向既存的Connection物件之关连。接下来,客户端可以设定Command物件的CommandText属性,其中包含查询,然後呼叫物件的Execute method。就像是Connection物件的Execute method一样,若这个查询回传资料,则这个呼叫会回传一个介面指标到一个新建立的Recordset物件。或者CommandText属性也可以包含一个预存程序或其它东西 它不见得一定要文字叙述的SQL查询。</span><span id=Layer60></font></p><p><font size=2 color=#3c3c3c face=arial>客户端可以呼叫一个Command物件的Execute method 以便执行储存在这个物件的查询</span><span id=Layer61></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>客户端可以呼叫一个Command物件的Execute method 以便执行储存在这个物件的查询</span><span id=Layer62></font></p><hr><p><font size=2 color=#3c3c3c face=arial>图6-4未显示一个Command物件也可以用来定义有参数的查询。这些参数可套用到定义在物件CommandText属性的查询,或者它们也可以是已在资料库建立预存程序之参数。为了达到这个目的,客户端呼叫Command物件的CreateParameter method,传入了描述这个参数的描述值。接下来客户端附加这个新Parameter物件到Command物件并执行这个查询。稍後执行这个相同的查询时,客户端可以只更改一个或多个Parameters的值,而不必建立一个全新的查询。</span><span id=Layer63></font></p><p><font size=2 color=#3c3c3c face=arial>Command物件中的查询可以拥有参数</span><span id=Layer64></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>Command物件中的查询可以拥有参数</span><span id=Layer65></font></p><hr><br><center><a target=_new href=imagesh/6-4.gif><img border=0 src='imagesl/6-4.gif'></a></center></span><span id=Layer66><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图 6-4</span><span id=Layer67> </b></font>一个ADO客户端也可以在一个Command物件的CommandText属性储存一个查询,然後呼叫物件的Execute method。</span><span id=Layer68></td></table></font></center><p><font size=2 color=#3c3c3c face=arial>透过设定在Command物件上其它属性的动作,ADO客户端便可控制指令执行时许多不同的方面。举例来说,客户端将Command物件的Prepared属性设定为TRUE,当指令第一次执行时,引导资料来源编译这个查询。虽然这个动作会使查询第一次执行时较为慢些,不过接续的要求将会变得较为快速,因为它们用的是已编译过的版本。客户端也可以设定查询Command物件之CommandTimeout属性,以控制ADO介面等待特定查询回应的时间长短。</span><span id=Layer69></font></p><p><font size=2 color=#3c3c3c face=arial>最後,ADO还提供一种方式来定义与执行查询:Recordset物件的Open method。客户端可以使用很多种方式指定欲进行的查询,包含传递一个SQL叙述,辨识包含一个查询既存之Command物件,或是预存程序。客户端同样也传入一个连线字串或一个指向既存Connection物件的参考,还有一些参数。如图6-5,查询将会被执行,若有回传结果,结果将会放在被呼叫的method之Recordset物件。</span><span id=Layer70></font></p><p><font size=2 color=#3c3c3c face=arial>你可以使用Recordset物件的Open Method执行查询</span><span id=Layer71></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>你可以使用Recordset物件的Open Method执行查询</span><span id=Layer72></font></p><hr><br><center><a target=_new href=imagesh/6-5.gif><img border=0 src='imagesl/6-5.gif'></a></center></span><span id=Layer73><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图 6-5</span><span id=Layer74> </b></font>ADO客户端可以使用Recordset的Open method同时开启一个连线,并执行一个查询。</span><span id=Layer75></td></table></font></center><font color=#3e72d7 face=arial size=4><b>检视查询的结果</span><span id=Layer76></b></font><p><font size=2 color=#3c3c3c face=arial>Recordset是ADO的基本,因为它们提供客户端最常用的资料检视。在前面描述的情节中,不管一个查询是如何被执行的,它回传的资料是包含在一个Recordset物件中。这样的结果让Recordset成为 ADO中最复杂的物件,拥有过多的属性(fields)与methods。因为它是ADO运作时的主要核心,所以值得详细探讨之。</span><span id=Layer77></font></p><p><font size=2 color=#3c3c3c face=arial>ADO客户端最常与储存在Recordset中的资料一起运作</span><span id=Layer78></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>ADO客户端最常与储存在Recordset中的资料一起运作</span><span id=Layer79></font></p><hr><p><font size=2 color=#3c3c3c face=arial>概念上,一个Recordset物件包含某些记录,每笔记录包含一个或多个栏位。每个Recordset物件都有一个cursor,允许客户端巡览物件中的记录。ADO定义许多不同类型的cursor,而能否使用某些类型的cursor是依赖於所使用的资料来源而定。Cursor永远指向Recordset中特定的记录,而客户端可以呼叫Recordset物件上的method来更改指向的记录。举例来说,MoveNext与MovePrevious会分别将cursor往前、往後移动到Recordset的记录。而MoveFirst与MoveLast会将cursor移动到第一与最後一笔记录。客户端也可以使用Recordset的Move method将cursor往前或往後移动特定数目的记录笔数。或者使用AbsolutePosition method将cursor移动到Recoreset中特定的记录。图6-6展示了某个查询回传Recordset内容之逻辑观点。这个物件有四笔记录,每个记录有叁个栏位,它的cursor目前指向Recordset的第二笔记录。</span><span id=Layer80></font></p><p><font size=2 color=#3c3c3c face=arial>每个Record-set都提供method让客户端可以和它包含的资料一起运作</span><span id=Layer81></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>每个Record-set都提供method让客户端可以和它包含的资料一起运作</span><span id=Layer82></font></p><hr><br><center><a target=_new href=imagesh/6-6.gif><img border=0 src='imagesl/6-6.gif'></a></center></span><span id=Layer83><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图 6-6</span><span id=Layer84> </b></font>Recordset物件包含一些记录,每笔记录包含一或多个栏位。Recordset的cursor 指向目前的记录。</span><span id=Layer85></td></table></font></center><p><font size=2 color=#3c3c3c face=arial>一旦客户端拥有指向参照到这个Recordset物件的介面指标後,原则上它希望存取这个物件的资料。其中一种存取的方法便是呼叫Recordset的GetRows method。依照客户端的决定,这个method复制Recordset所有或部份的内容,然後放到客户端提供的二维阵列中。客户端可以依照自己的意愿来检视阵列的内容。</span><span id=Layer86></font></p><p><font size=2 color=#3c3c3c face=arial>一个更常用来检视Recordset内容的方法,是个别检查每笔横列的栏位(field)。若要达到这个目的,每个Recordset物件都有一个关联到自己的Fields集合物件。这个物件依次参考到Recordset中每个直栏(column)的Field物件。在任何时间点,Recordset的Fields集合所参考到的栏位就是cursor目前所指向的栏位。</span><span id=Layer87></font></p><p><font size=2 color=#3c3c3c face=arial>Recordset中目前的资料可透过Record-set的Fields集合来存取</span><span id=Layer88></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>Recordset中目前的资料可透过Record-set的Fields集合来存取</span><span id=Layer89></font></p><hr><p><font size=2 color=#3c3c3c face=arial>举例来说,假设一个Recordset的内容以及cursor目前的位置如图6-6所示,Fields与Field物件如图6-7。为了要存取Recordset中横列的值,客户端可以巡览Fields集合物件,然後存取每个对应的Field之属性值。其中最重要的属性包含Name与Value,不用讶异,它们分别包含栏位的名称与值。在图6-7,第一个Field物件的Name属性包含「Customer」,而Value属性包含「Ali」。当ADO客户端移动Recordset中的cursor到其它记录时,Field物件的值就会变更,反应出目前记录中的值。这个过程看起来似乎有点累赘,不过实际上对使用这个程式码的客户端来说是相当简单而直觉的。</span><span id=Layer90></font></p><br><center><a target=_new href=imagesh/6-7.gif><img border=0 src='imagesl/6-7.gif'></a></center></span><span id=Layer91><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图6-7</span><span id=Layer92> </b></font>Recordset的Field集合参照到一个Field物件群组,每一个物件在目前的记录里面都包含一个栏位的值。</span><span id=Layer93></td></table></font></center><font color=#3e72d7 face=arial size=4><b>保存Recordset</span><span id=Layer94></b></font><p><font size=2 color=#3c3c3c face=arial>一旦Recordset建立并繁衍(populate)後,ADO客户端可以使用OLE DB Persistence Provider将Recordset的内容储存到硬碟中的档案。为了达到这个目的,客户端呼叫Recordset的Save method,指明欲储存的位置,以及储存的格式当做传入的参数。举例来说,客户端可能指定储存在本机的档案名称,或在远端Web伺服器上的一个URL。你可以将Recordset储存成两种格式:Microsoft的Advanced Data TableGram (ADTG)格式,它是预设的选项,或者使用延伸标记语言(Extensible Markup Language,XML)的格式。</span><span id=Layer95></font></p><p><font size=2 color=#3c3c3c face=arial>一个Record-set的资料可以储存到硬碟</span><span id=Layer96></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>一个Record-set的资料可以储存到硬碟</span><span id=Layer97></font></p><hr><p><font size=2 color=#3c3c3c face=arial>若要从一个已储存的Recordset中取回这个资讯,客户端可以呼叫Recordset物件的Open method,传递参数已指Recordset资料所在的位置,如一个档案名称。保存在Recordset中的资讯将会被复制到Open method所建立的Recordset中。你也可以使用另一种方法,客户端可以呼叫Connection物件的Execute method,传入档案名称或其它可指明保存的Recordset所在位置之资讯。在这个案例下,这个呼叫将回传包含保存的Recordset内含的资讯。</span><span id=Layer98></font></p><p><font size=2 color=#3c3c3c face=arial>一个已储存的Recordset资料可以再载入成为一个新Recordset</span><span id=Layer99></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>一个已储存的Recordset资料可以再载入成为一个新Recordset</span><span id=Layer100></font></p><hr><font color=#3e72d7 face=arial size=4><b>修改资料</span><span id=Layer101></b></font><p><font size=2 color=#3c3c3c face=arial>若要读取查询的结果,ADO客户端可以检视包含在Recordset物件中的值,如前所描述。客户端也可以设定这些值,允许修改资料来源中的资料。运作的过程如图6-8。</span><span id=Layer102></font></p><br><center><a target=_new href=imagesh/6-8.gif><img border=0 src='imagesl/6-8.gif'></a></center></span><span id=Layer103><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图 6-8</span><span id=Layer104> </b></font>客户端可以建立并繁衍一个Recordset物件,更改它包含的值,然後将变动的部份更新底层的资料来源。</span><span id=Layer105></td></table></font></center><p><font size=2 color=#3c3c3c face=arial>这个过程开始於建立一个Recordset并繁衍它的内容。这个动作可以透过Connection或Command物件执行的查询所取回结果来达成。或者也可以明确地建立并繁衍一个Recordset物件。一旦客户端拥有一个指向此Recordset的指标後,它便可以透过前面描述的Fields集合取回它的值。不过客户端也可以更改这些值,只要设定每个Field物件的Value属性就可以了。使用cursor巡览Recordset中的记录,客户端可以在多个Recordset一次更改多个值。一旦所有的更改动作完成,客户端可以呼叫Recordset的Update method,则底层资料来源的值便变更为新的值。(当然,还有其它的方式可以达到这个目的:客户端可以直接传递一对或多对栏位名称/值的组合做为Update呼叫的参数。)</span><span id=Layer106></font></p><p><font size=2 color=#3c3c3c face=arial>客户端可以修改Recordset中的资料,然後将这些变动套用到资料来源</span><span id=Layer107></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>客户端可以修改Recordset中的资料,然後将这些变动套用到资料来源</span><span id=Layer108></font></p><hr><p><font size=2 color=#3c3c3c face=arial>客户端也可能不使用Recordset物件来修改底层资料库的资料。举例来说,假设底层资料库是一个关联式资料库,客户端可以使用一般的SQL UPDATE叙述(假设使用Command物件执行之)修改储存的资料。在许多情况下,如典型的关联式资料库,这类的更新动作永远都不够使用。</span><span id=Layer109></font></p><font color=#3e72d7 face=arial size=4><b>锁定资料</span><span id=Layer110></b></font><p><font size=2 color=#3c3c3c face=arial>当客户端第一次检视已繁衍的Recordset物件时,物件中的资料通常都是从底层资料库取得的部份某些资料。当客户端在使用Recordset中的资料时,某些客户端也可能对资料库中相同的资料感兴趣,也想进行读取或修改的动作。但你允许其它的客户端做这样的事吗?在某些情况下,允许其它的客户端存取底层资料将会导致一些问题。举例来说,若这两个客户端只是读取资料,此时并没有不让他们同时存取相同资讯的理由。若这两个客户端同时想更改资料,或者其中一个客户端希望读取资料,而另一个客户端想修改资料,此时允许同时进行存取的动作将会带来大麻烦。</span><span id=Layer111></font></p><p><font size=2 color=#3c3c3c face=arial>客户端通常希望锁定资料以供自己独占使用</span><span id=Layer112></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>客户端通常希望锁定资料以供自己独占使用</span><span id=Layer113></font></p><hr><p><font size=2 color=#3c3c3c face=arial>为了避免这些问题,客户端可以设定Recordset的LockType属性。这个值要在Recordset开启之前先设定,它有四个可能的值:</span><span id=Layer114></font></p><font size=2 color=#3c3c3c face=arial><ul><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> 唯读</span><span id=Layer115> </b></font>(</span><span id=Layer116><font size=2 face=arial color=#3e80d7><b> Readonly</span><span id=Layer117> </b></font>) 预设值。客户端不可以更改Recordset中的值,资料来源并不会锁定底层的资料。若LockType属性设定为这个值,则复制到Recordset资料的其它客户端仍旧可以读取、修改资料来源的资料。</span><span id=Layer118></li><br></font><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> 悲观锁定</span><span id=Layer119> </b></font>(</span><span id=Layer120><font size=2 face=arial color=#3e80d7><b> Pessimistic</span><span id=Layer121> </b></font>) 当Recordset的客户端第一次修改此记户中的栏位时,资料来源会锁定资料库中的记录。没有客户端可以读取或修改已锁定的资料库记录,直到客户端呼叫Recordset的Update method为止。这个选项确保在客户端修改Recordset中的资料复本时,底层资料库的资料不能被改变。它同时也确保Recordset中变动的资料稍後套用到底层资料来源的动作会成功地执行。虽然它会降低执行效能,因为其它客户端希望修改或读取的这些记录被锁定了,直到客户端已完成它对Recordset的操作为止。</span><span id=Layer122></li><br></font><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> 乐观锁定</span><span id=Layer123> </b></font>(</span><span id=Layer124><font size=2 face=arial color=#3e80d7><b> Optimistic</span><span id=Layer125> </b></font>) 当将资料读取到Recordset时,资料来源并不会锁定资料库的记录,不过客户端仍旧可以自由地更改Recordset中的值。当客户端呼叫这个Recordset的Update method时,资料来源立即锁定资料库中欲更改的那一笔记录。这产生了一个很明显的问题:若此刻其它的客户端已更改了记录中的值时会发生什麽事?这也就是为何这个方案叫做「乐观」的原因了。客户端并不希望这个情况发生。若事实已发生,资料来源会针对每笔已被更动的横列,以及更新将会失败的横列,回传一个错误讯息给客户端。乐观锁定的目的是希望增加同时可以存取资料的客户端数目,以及增加使用这个资料库的应用程式之执行效能。若客户端必须永远都看到每笔记录最新的值,或不希望处理可能产生的错误,悲观锁定是较好的选择。</span><span id=Layer126></li><br></font><font size=2 face=arial color=#3c3c3c><li><font size=2 face=arial color=#3e80d7><b> 批次乐观锁定</span><span id=Layer127> </b></font>(</span><span id=Layer128><font size=2 face=arial color=#3e80d7><b> BatchOptimistic</span><span id=Layer129> </b></font>) 这个选项用在UpdateBatch method以及离线Recordset(disconnected Recordset)上,将於本章稍後描述。</span><span id=Layer130></li><br></font></ul></font><p><font size=2 color=#3c3c3c face=arial>Recordset的LockType属性可以用来锁定资料</span><span id=Layer131></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>Recordset的LockType属性可以用来锁定资料</span><span id=Layer132></font></p><hr><p><font size=2 color=#3c3c3c face=arial>一个特定的资料来源很可能没有完全支援四个LockType选项,因此可使用哪种视情况而定。然而选择最佳的锁定选项仍是建立一个应用程式最重要的一部份,以便能够产出正确的执行结果。</span><span id=Layer133></font></p><font color=#3e72d7 face=arial size=4><b>使用交易</span><span id=Layer134></b></font><p><font size=2 color=#3c3c3c face=arial>简单的说,一个交易就是指一堆事件,这些事件不是全部发生,就是全部没有发生。一个交易最常见的例子便是发生在客户端必须更新两次或多次资料库中的资料。举例来说,假设QwickBank顾客希望从他们的存款帐户转帐$100到它们的支票帐户。要完成这个工作需要从顾客的存款帐户减取$100,然後在支票帐户加上相同的金额。这两个事件很可能纯粹更改资料库的资料,因为今天金钱款项不过就是硬碟中的数据罢了。倘若只有其中一个事件发生,那麽对顾客或是对银行来说都会不高兴 某些人的钱就是不见了。为了要产生一致的结果,这个结果要让顾客以及银行都能满意,则两个动作必须执行,或没有一个动作可以执行。换句话说,这两个事件必须属於一个单独的交易。</span><span id=Layer135></font></p><p><font size=2 color=#3c3c3c face=arial>交易是一个all-or-nothing的事件:不是全部成功便是全部失败</span><span id=Layer136></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>交易是一个all-or-nothing的事件:不是全部成功便是全部失败</span><span id=Layer137></font></p><hr><p><font size=2 color=#3c3c3c face=arial>当一个交易开始或结束时,ADO允许客户端通知资料来源。通常ADO客户端要求要开始一个交易是透过DBMS实作的交易管理员函数完成的。客户端在这个资料库执行的所有工作,除非明确指定,否则都视为这个交易的一部份。当客户端完成交易中的操作时,它拥有两个选择。若它告诉DBMS确认交易,则执行的所有动作都会永久的改变。若它告诉DBMS取消交易,客户端在这个交易中进行的所有变动都要复原,资料库将会回复到交易开始之前的状态。实际上客户端可以界定交易的界线,指明该包含哪个操作,而DBMS本身则确保交易是一个全部成功或全部失败的事情。</span><span id=Layer138></font></p><p><font size=2 color=#3c3c3c face=arial>ADO客户端可以依赖资料来源来提供交易</span><span id=Layer139></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>ADO客户端可以依赖资料来源来提供交易</span><span id=Layer140></font></p><hr><p><font size=2 color=#3c3c3c face=arial>如图6-9,ADO客户端可以在交易执行所在的Connection物件上呼叫BeginTrans method开始一个新交易。这个要求会转送到底层资料来源的交易管理者函数,然後进行在这个连线上要启动一个交易时必要的作业。然後客户端发行一个查询,这个查询也可以是这个交易的一部份。这些查询可使用许多方式产生,如前所描述。而所有的查询都是使用同一个Connection物件。在执行完交易中所有的作业後,客户端便呼叫Connection物件上的CommitTrans或RollbackTrans method。这两个method告诉资料来源分别确认或取消这个交易,资料来源再一次进行必要作业以适当地结述这个交易。</span><span id=Layer141></font></p><p><font size=2 color=#3c3c3c face=arial>客户端可以使用ADO Connection物件控制资料来源中的交易</span><span id=Layer142></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>客户端可以使用ADO Connection物件控制资料来源中的交易</span><span id=Layer143></font></p><hr><p><font size=2 color=#3c3c3c face=arial>对客户端来说循序使用很多交易是很平常的。为了要让这个工作更为简单,ADO客户端可以设定Connection物件的Attributes属性,强制在目前的交易结束时,自动产生一个新交易,这也就是说,在客户端的CommitTrans或RollbackTrans呼叫完成时。如此将允许客户端执行一连串的交易,而不须重复地呼叫BeginTrans来开始每一个交易。</span><span id=Layer144></font></p><br><center><a target=_new href=imagesh/6-9.gif><img border=0 src='imagesl/6-9.gif'></a></center></span><span id=Layer145><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图6-9</span><span id=Layer146> </b></font>ADO客户端可以依赖DBMS内建的交易管理员来确保交易中所有的动作同时发生或全部都没有发生。</span><span id=Layer147></td></table></font></center><p><font size=2 color=#3c3c3c face=arial>一般而言,资料库中的交易隐含锁定的意味 隔离 在这个交易中存取到的资料。举例来说,想像一下一个ADO客户端希望在两个银行帐户之间进行转帐。如前所描述,要完成这个工作需要在一个帐户中增加金额,在另一个帐户中减少金额。但是,若交易中牵涉到的资料 (这两个帐户的馀额)在任何时间仍旧可让其它客户端存取,另一个客户端可能会在转帐交易的过程中检视帐户馀额,更糟的是更改馀额的值。若这个交易复原了,则第二个客户端读取到的值可能是无效的值,有效与无效是取决於读取这个值的时间点。同时若第二个客户端修改任一个帐户的馀额,这个变动的资料可能会遗失,因为第一个交易中可能定义了在交易复原时,将帐户馀额回存成原始的值。底线在於一个交易应该能够安全地锁定使用中的资料,避免其它客户端的存取。</span><span id=Layer148></font></p><p><font size=2 color=#3c3c3c face=arial>交易一般来说隐含锁定资料的意味</span><span id=Layer149></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>交易一般来说隐含锁定资料的意味</span><span id=Layer150></font></p><hr><p><font size=2 color=#3c3c3c face=arial>但是要达到好的执行效能有时需要将规则制定宽松一点。尤其是在某些情况下,客户端在某个交易中存取的资料也可以是目前正在执行的另一个交易正在处理的资料。若要对它们进行控制,客户端可以设定交易执行所在的Connection物件之IsolationLevel属性。客户端设定在这个属性上的值会影响使用这个Connection物件来执行的交易之所有行为。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -