用VB與RDO訪問SQL Server
發表時間:2023-08-17 來源:明輝站整理相關軟件相關文章人氣:
[摘要]RDO的發展 遠程數據對象 (Remote Data Object ,簡稱RDO) 是位于 ODBC API 之上的一個對象模型薄層,它依賴 ODBC API、選定的 ODBC 驅動程序以及后...
RDO的發展
遠程數據對象 (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 AP I在速度方面性能很好,但它需要花費很大的精力進行繁瑣的編程工作。 當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-SQL、T-SQL調試集成在開發環境中、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等普遍應用的Activ eX控件不同的是,RDO沒有自己的用戶界面,因而可以和VB標準的Timer控件歸為同一類。當然也可以將RDO看作調用ODBC API函數,進而對后臺數據庫操作加以控制的對象。在RDO的屬性和方法中,包含了對單個的ODBC API函數以及一連串API函數的調用。
1. rdoEngine對象 最初調用RDO對象以及RDC(遠程數據控件)時,自動生成rdoEngine對象的附帶事件(i ncident)。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集合的已經存在的成員名稱相同,會產生錯誤。新建的rdoEnvi ronment對象自動追加在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的連接和斷開的功能,需由程序員自己加以判斷。這里需要注意的是對連接和斷開時機的管理,因為同時有過多的連接將會造成服務器負載過重。而且,對SQL Server而言,一個連接只能同時支持一個操作,當同時進行記錄的讀出和更新時,需要分別對其各自的連接加以確認。在一定條件下需要保持連接;反之,當操作完成以后,又需要立即將連接斷開。如果能正確地設計好連接和斷開的時刻,就有可能確保擴展更多的用戶數(客戶/服務器開發環境中,與數據庫項的可擴展性一樣,客戶數擴張的可能性也非常重要)。 由于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方法,我們將在下期中介紹這些方法。此外,下面我們還將通過實際代碼向大家繼續介紹經DSN對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。只是最好不要讓一般用戶隨意更改這個設定。通常情況下,正確的處理方法是設定為rdDriverNoPrompt,利用Error處理程序編程進行處理。
ReadOnly,若需要通過連接對數據進行更新時設定為False。若沒有數據更新的必要則設為True,這時因為ODBC驅動沒有對數據更新進行管理的需要,可以提高程序效率。
Connect,向ODBC驅動管理器傳遞所有的ODBC連接參數。可以省略dsName,只通過Co nnect參數進行包括用戶名、口令、缺省數據庫、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方法同OpenConnection方法很相似,被用于停止狀態或獨立的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 If
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控件也都是對象。使用對象后必須養成將對象設為Nothi ng把它從內存中釋放的編程習慣。這樣可以預防很多不可預測錯誤,往往程序中發生意義不明的錯誤時,其原因就在于此。
數據的取得
與數據庫連接成功之后,接下來就是取得數據。一般用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對象或rdoQuery對象的OpenResultset方法創建rdoResultset對象。
* rdoQuery對象,與DAO的QueryDef相似,在進行一次性查詢時使用,用于取代已經過時的由rdoConnection對象的CreatePreparedStatement方法創建的rdoPreparedStatement對象。rdoQuery對象直接調用ODBC的SQLPrepare。
* UserConnection對象,在VB5.0以及RDO 2.0中新增加的對象,這里不作詳細闡述。它是一個非常優秀的對象,使RDO使用起來更加容易。
* rdoTable對象,是rdoTables集合的成員,用來顯示SQL Sever上的一個表的內容。
編程中通常運用前3種方法中的某一種,而第4種一般不太用。
不論使用哪一種方法,都有必要給出SQL語句。在絕大多數的情況下,SQL語句是針對SQL Server,從哪個表以何種條件讀取哪一行的數據,如(SELECT * FROM table1 WHERE field1 = “condition”)。運行存儲過程時用Name參數指定存儲過程,程序能自動判別是RDO 2.0的還是存儲過程的SQL語句。在以存儲過程為核心的情況下,利用UserConnection將存儲過程作為方法來處理最為妥當。
下面給出一個運行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驅動器進行自動識別。使用rdoParameters(n).Direction可以對返回值(rdParamReturnValue)、輸入參數 (rdParamInput) 、輸出參數(rdParamOutput)和輸入輸出參數(rdParamInputOutput)加以控制。但通常ODBC都會讀入存儲過程的定義式,并加以正確識別,所以絕大多數的情況下不必使用這個參數。
Dim CPw = Cn.CreateQuery(“GetVendorCount”, sSQL)
代碼生成名為GetVendorCount的rdoQuery對象,并將rdoQuery對象自動增加到rdoQueries集合中,以后可以重復使用。
現在將CPw對象的第一個參數指定為返回值:
CPw.rdoParameters(0).Direction = rdParamReturnValue
最后由Execute方法運行:
CPw.Execute
返回行的查詢(存儲過程中包含一個以上的SELECT)時,可使用OpenResultset方法。運行后,可通過rdoParameters集合取得返回值:
If CPw.rdoParameters(0) > 0 Then
MsgBox CPw.rdoParameters & “數據取得成功”
Else
MsgBox “數據讀取失敗”
End If
數據的追加、更新、刪除
對SQL語句已經有一定了解的讀者,應該比較熟悉INSERT、UPDATE、DELETE等語句。對于rdoConnection對象,雖然可以在OpenResultset的Name參數中直接代入SQL語句,用Execute方法運行,但沒有充分利用RDO對象的長處。在rdoResultset中有AddNew、Edit、Update、Delete、MoveNext、MovePrevious、MoveFirst、MoveLast方法,與DAO/Jet相似,用起來非常便利。
下面在先前的Resultset的例子中追加記錄:
Rs.AddNew
Rs(“au_id”) = “111-46-1992”
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或MoveNext操作:
Rs.ReQuery 或 Rs.MoveNext
以上簡單地說明了數據的增加、更新、刪除方法,根據數據庫的模式的不同,增加、更新會變得非常復雜。另外,由于數據表的原因,有時會使得刪除操作變得復雜。通常與數據表的構造、相關性有關的處理,為了使客戶端的代碼盡可能簡潔,應在SQL Server上創建觸發器。有關觸發器的內容已超出了本文討論的范圍,這里不詳細說明。本文介紹的只是一些基本的操作方法,RDO數據處理功能不僅限于此,讀者可在實際開發中進一步領會。
總結
RDO是開發數據庫應用程序功能強大的對象方法,要真正做到應用自如,需要付出很大努力。本文描繪了RDO基本的構成、功能、編程方式,希望讀者由此對數據庫編程方式以及RDO的使用有更為充分的了解。如果需要進一步研究,建議可以從以下幾方面入手:
* SQL Server(或Oracle)的功能,特別是存儲過程、View、觸發器、安全模式等;
* 數據庫設計基礎;
* SQL語句;
* Visual Basic的對象概念。
這些粗看起來與RDO沒有什么聯系,但實際上有助于對RDO的結構、原理等基本技術的理解。換而言之,學習數據庫編程的基本內容為大前提,RDO或者ADO的應用不過是訪問數據庫的一種手段而已。