8.3.3 使用连接状态
将连接字符串存入应用程序变量是一个常用的技巧,同使用一个包含文件一样有效。例如,可以在global.asa文件中加入下面的代码:
Sub Application_OnStart()
strConn = "Provider=SQLOLEDB; Data Source=WATCHER; " & _
"Initial Catalog=pubs; User Id=davids; Password=whisky"
Set Application("ConnectionString") = strConn
End Sub
在ASP页面中,可以使用下面的代码:
Set conPubs = Server.CreateObject("ADODB.Connection")
conPubs.Application("ConnectionString")
从个人的角度,我更喜欢使用包含文件的方法,因为我写了许多不同的连接到各种服务器和数据库的例子。使用应用程序方法将意味着每次必须关闭浏览器重新启动应用程序。读者可以使用自己喜欢的任一种方法,在速度上它们并没有差别。
对于在本书的这节内的例子,将使用一个含有连接字符串的connection.asp文件人作为一个包含文件。
8.3.4 连接语法
上面所叙述的是相关理论,当确实要与数据存储连接时,应该怎么办?如果使用显式定义的Connection对象,可以使用Open方法,它的语法如下:
connection.Open [ConnectionString], [UserID], [Password], [Options]
参 数
说 明
ConnectionString
包含连接细节的字符串。可以是ODBC DSN的名称、数据链接文件的名称或真实的连接细节
UserID
连接期间,用户使用的名字。覆盖连接字符串中提供的任何用户名
Password
用户的口令。覆盖连接字符串中提供的任何口令
Options
可以是adAsyncConnect,指定异步地建立连接。忽略这个参数,则建立一个同步连接
异步连接不用于ASP环境,因为脚本语言不能接收来自ADO的事件。
8.3.5 连接的例子
下面是几个示例,这里假定strConn包含一个有效的连接字符串。
为了打开一个连接,使用Connection对象的Open方法。例如:
Set conPubs = Server.Connection("ADODB.Connection")
conPubs.Open strConn
' Some processing
conPubs.Close
也可以使用ConnectionString属性:
Set conPubs = Server.CreateObject("ADODB.Connection")
conPubs.ConnectionString = strConn
conPubs.Open
' Some processing
conPubs.Close
这两种实现方法之间没有什么区别,如果使用前一种方法来实现连接,ConnectionString属性同时也被赋值。
值得注意的是,一旦与数据存储建立了连接,ADO可能会改变ConnectionString属性值。不必担心,ADO只填写一些额外的属性值。
8.3.6 连接缓冲池
连接缓冲池(connection pool)总使许多人感到困惑,其实原理非常简单。当关闭一个连接,就用户(和ADO)而言,这个连接已经关闭。但实际上OLE DB并没有关闭这个连接,只是将其放入了非活动的连接缓冲池中。任何时候用户(或其他人)打开一个连接,OLE DB首先检测连接缓冲池中是否有相同连接细节的连接存在。如果有,将直接从缓冲池中取得此连接。如果没有,则为用户创建一个新的连接。为了避免浪费资源,经过一段缺省的时间段后,就从缓冲池中清除该连接。
那么,它的优点在哪里?打开一个连接可能是所进行的操作中最慢的操作之一,连接缓冲池使用户能与数据存储再次连接而无须重新创建连接。这对于那些连续打开和关闭大量连接的Web站点显得特别重要。
对于ODBC连接,连接缓冲池由ODBC Data Source Administrator控制。对于OLE DB,不能改变连接缓冲池(或叫会话缓冲池)。
必须注意的是,连接缓冲池不是连接共享。一个连接只有在被客户关闭后才能再次使用。
内务处理
为了使连接缓冲池生效,必须确保内务处理(Housekepping)处于有序状态。这包括及时关闭Connection对象,这样它们才能回到缓冲池重新使用。你可能认为不断地打开、关闭连接对系统的开销很大,但必须衡量一下可扩展性——你的应用程序可能有许多人在使用,OLE DB又非常善于管理连接资源。
一般的原则是:尽可能晚地建立连接,同时又要尽可能早地关闭连接,这样保证连接打开的时间段最短。
8.4 记录集
前面已经提到,记录集是ADO中最常用的对象,这并不值得奇怪。毕竟,他们包含着数据。但是,对于记录集还有比想象的更多的内容,知道数据如何保存和处理很重要,因为这为选择使用哪种记录集提供了更多的参考。
记录集有不同的类型,在一些细小的地方存在着差异,很容易造成失误。首先需要认真谈论的是光标的概念。
||||||8.4.1 光标
光标(cursor)是让许多人感到困惑的概念,但实际上非常的简单。
光标用来管理记录集和记录集的当前位置,后者是由当前记录指针来处理的。
这不是Recordset对象所做的吗?是的,但是记录集也是依靠它的光标。这仍然没有回答光标是什么这个问题,那么先来看一个
AU_ID
AU_LNAME
AU_FNAME
PHONE
172-32-1176
White
Bob
408 496-7223
219-46-8915
Green
Marjorie
415 986-7020
238-95-7766
Carson
Cheryl
415 548-7723
267-41-2394
O'Leary
Michael
408 286-2428
274-80-9391
Straight
Dean
415 834-2919
341-22-1782
Smith
Meander
913 843-0462
这里有六行四列。打开一个记录集,当前记录就是第一个记录,即为Bob White的那条记录。用什么来标识当前记录?用当前记录指针。那么又如何处理这个指针呢?当需要移到下一条记录或者是其他记录时,是通过光标来实现的。在访问当前行的字段时,光标知道目前位于哪一行,所以能返回正确的值。如果试图移出记录集的最后一行,光标也会处理。
理解光标的一种好方法是将光标想象成为一个可以在记录集内移动的窗口。这一窗口与记录集的单个行同样高,同样长,因此一次只能看到一行数据值。当你移到另一条记录时,这个窗口也跟着移动。
也许你认为这相当简单,但它确实很重要,因为能用光标做什么是由光标的类型决定的。
1. 光标类型
光标的类型标识了光标所能够提供的功能。这里有四种类型的光标:
· 静态(adOpenStatic)。静态光标含有对记录的静态拷贝。这意味着在记录集建立之后,记录集的内容就固定了。其他用户对记录的更改、添加和删除都是不可见的。允许在记录集中向前、向后移动。
· 只许前移(adOpenForwardOnly)。缺省的光标类型,除了只允许向前移动外,其余的与静态光标相同。
· 动态(adOpenDynamic)。动态的光标没有固定的记录集。其他用户的更改、添加或删除操作在记录集中是可见的。允许在记录集中向前、向后移动。
· 键集(adOpenKeyset)。键集类型的光标除了记录集是固定的,其余的与动态光标相似。可以看到其他用户的修改,但新记录却不可见。如果别的用户删除了记录,那么这些记录在记录集中将会变得不可访问。这项功能是通过标识记录集的键来实现的,所以键一直保留着,即使改变或删除记录。
为了理解这些概念,再想象光标窗口。对于只许前移的光标,可以看作是一个位于单向齿轮上的窗口,只能向前移动。这一特点的有利之处在于一旦通过了一条记录,光标就会完全忘记该记录,因为永远不会回到该记录上。静态光标则移去了单向齿轮,允许向后移动;因为也能向后移动,光标需要跟踪这些记录。由于这个原因,静态光标比只许前移的光标慢。
对于键集和动态类型的光标,窗口可以前后移动,但所看到的内容可能会改变。键集光标可以看到别人对数据的更改,但看不到新的或已删除的记录。因此,记录集是固定的,但不是内容固定。动态光标将它扩展了,不仅可以改变记录的内容,而且可以改变记录集。所以在动态光标中能够看到有新的记录出现,同时删除的记录从记录集中消失。
使用的光标类型取决于想达到的目的。如果只想浏览记录,也许是为了创建一个表格或一个选择列表,那么用只许前移的光标是最好不过了。虽然使用其他类型的光标速度可能会慢一些,但也可以正常工作。
光标的类型会影响性能,特别是服务器光标。例如,在微软的SQL Server 6.5中,键集和静态类型的光标都需要在临时数据库(tempdb)中放入一个完整的数据拷贝。其中,键集类型的光标相比较而言稍微高效一些,因为它只将键拷入临时数据库。对于SQL Server 7.0情况不是这样,不同类型的光标的运行效率差别不是很大。
2. 光标位置
既然已经解释了什么是光标,以及光标如何管理数据,但是光标在哪里呢?答案不是固定的,因为光标依赖于数据存储。某些数据存储,比如微软的SQL Server,有自身的光标服务;而别的如微软的Access却没有光标服务。
当打开一个记录集时,必须选择是否希望数据存储管理光标,或是希望OLE DB和ADO在本地为你管理光标。后者可以实现是因为OLE DB有其自己的光标服务。通过使用Connection对象或Recordset对象的CursorLocation属性可以设置这两个选项。可以设定该属性的值为:
· adUseServer:让数据存储管理光标。
· adUseClient:让ADO管理光标。
可以在打开连接或记录集之前设置这个属性:
conPubs.CursorLocation = adUseServer
conPubs.Open strConn
或者:
rsAuthors.CursorLocation = adUseClient
rsAuthors.Open "authors", conPubs
缺省的光标是基于服务器的,理解这两种类型的区别非常重要。对于一个服务器光标来说,数据存储的任务是管理记录,所以,当使用服务器光标建立一个记录集时,数据存储管理着记录的移动、记录的更新等等。
对于一个客户光标,记录集的全部内容复制给客户,受本地客户光标服务管理。这意味着对于一个客户光标,打开一个具有大量记录的记录集要比使用基于服务器的光标打开相同记录集所花费的时间长得多。也需要使用基于客户的光标的时候,在本书后面,研究组件时,会看到更多的相关的例子。
3. “消防带”光标
你可能知道“消防带”(Firehose)光标,由于能给应用程序带来高的运行效率,所以对其进行解释显得非常重要。因为“消防带”光标是一种特殊类型的光标,只有在与微软的SQL Server连接时才出现。SQL Server创建用户请求的数据集,然后把数据直接传给客户以使其尽可能快地得到数据。SQL Server自身几乎没有光标管理,这意味着它可以更快地处理数据。也就是说数据可以在非常短的时间内迅速返回到客户端。从客户方看,类型于只许前移的光标。
那么,在前面讨论光标类型时,为什么没有涉及到“消防带”光标呢?因为这类光标专用于SQL Server,并仅用于使用基于服务器的光标时。这不是一种真正的光标类型,获得一个“消防带”类型光标的方法就是不指定光标的类型。
8.4.2 锁定
我们已经解释了光标和如何管理数据。现在可以创建记录集了吗?恐怕还不行,因为还有一个问题没有讨论,那就是锁定。
锁定就是如何确保数据的完整性,确保更改不会被覆盖。我们需要避免的典型情况是多次更新,比如一个用户改动了一些数据,接着另一个用户立即又将其做了修改。为了对这种情况加以保护,要锁定记录,有许多不同的方法可以保证记录得到保护。可通过锁定类型来设置这些方法。
锁定类型
锁定类型决定更新记录时记录是否或如何被锁定。有四种类型的锁定:
· 只读(adLockReadOnly):缺省锁定类型,记录集是只读的,不能修改记录。
· 悲观的(adLockPessimistic):当修改记录时,数据提供者将尝试锁定记录以确保成功地编辑记录。只要编辑一开始,则立即锁住记录。
· 乐观的(adLockOptimistic):直到用Update方法提交更新记录时才锁定记录。
· 批量乐观的(adLockBatchOptimistic):允许修改多个记录,只有调用UpdateBatch方法后才锁定记录。
当不需要改动任何记录时,应该使用只读的记录集,这样提供者不用做任何检测。对于一般的使用,乐观的锁定可能是最好的选择,因为记录只被锁定一小段时间,数据在这段时间被更新。这减少了资源的使用。
悲观的锁定提高了数据的完整性,但却是以牺牲并发性为代价的。并发性是许多用户在同一时间查阅数据的能力。锁定的记录对其他用户是不可见的,因而数据的并发性降低了。乐观的锁定只在一小段时间内锁定记录,所以增强了数据的并发性,但同时其他用户修改数据的几率也增加了。
关于并发性和锁定的问题在微软出版的《Inside SQL Server 7.0》(作者Ron Soukup和Kalen Delaney)中做了较好的论述。这是一本很有权威的专著,所以无论如何应购买这本专著,该书有大量的有价值的资料。