RDO和VB數據庫編程

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 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的應用不過是訪問數據庫的一種手段而已。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章