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]
參數如表8-1所示:
表8-1 Open方法的參數及說明
參 數
說 明
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對象所做的嗎?是的,但是記錄集也是依靠它的光標。這仍然沒有回答光標是什麼這個問題,那麼先來看一個記錄集,如表8-2所示:
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)中做了較好的論述。這是一本很有權威的專著,所以無論如何應購買這本專著,該書有大量的有價值的資料。