ADO
用戶資料庫中數據結構的數據
一種程序對象,用於表示用戶資料庫中的數據結構和所包含的數據。在Microsoft Visual Basic編輯器中,可以使用ADO對象以及ADO的附加組件(稱為Microsoft ADO Extensions for DLL and Security(ADOX))來創建或修改表和查詢、檢驗資料庫、或者訪問外部數據源。還可在代碼中使用ADO來操作資料庫中的數據。
ADO (ActiveX Data Objects,ActiveX數據對象)是Microsoft提出的應用程序介面(API)用以實現訪問關係或非關係資料庫中的數據。例如,如果您希望編寫應用程序從DB2或Oracle資料庫中向網頁提供數據,可以將ADO程序包括在作為活動伺服器頁(ASP)的HTML文件中。當用戶從網站請求網頁時,返回的網頁也包括了數據中的相應數據,這些是由於使用了ADO代碼的結果。
像Microsoft的其它系統介面一樣,ADO是面向對象的。它是Microsoft全局數據訪問(UDA)的一部分,Microsoft認為與其自己創建一個數據,不如利用UDA訪問已有的資料庫。為達到這一目的,Microsoft和其它資料庫公司在它們的資料庫和Microsoft的OLE資料庫之間提供了一個“橋”程序,OLE資料庫已經在使用ADO技術。ADO的一個特徵(稱為遠程數據服務)支持網頁中的數據相關的ActiveX控制項和有效的客戶端緩衝。作為ActiveX的一部分,ADO也是Microsoft的組件對象模式(COM)的一部分,它的面向組件的框架用以將程序組裝在一起。
ADO從原來的Microsoft數據介面遠程數據對象(RDO)而來。RDO與ODBC一起工作訪問關係資料庫,但不能訪問如ISAM和VSAM的非關係資料庫。
ADO 是對當前微軟所支持的資料庫進行操作的最有效和最簡單直接的方法,它是一種功能強大的數據訪問編程模式,從而使得大部分數據源可編程的屬性得以直接擴展到你的Active Server 頁面上。可以使用ADO 去編寫緊湊簡明的腳本以便連接到 Open Database Connectivity (ODBC) 兼容的資料庫和 OLE DB 兼容的數據源,這樣 ASP 程序員就可以訪問任何與 ODBC 兼容的資料庫,包括 MS SQL SERVER、Access、 Oracle等等。
比如,如果網站開發人員需要讓用戶通過訪問網頁來獲得存在於IBM DB2或者Oracle資料庫中的數據,那麼就可以在ASP頁面中包含ADO程序,用來連接資料庫。於是,當用戶在網站上瀏覽網頁時,返回的網頁將會包含從資料庫中獲取的數據。而這些數據都是由ADO代碼做到的。
ADO是一種面向對象的編程介面,微軟介紹說,與其同IBM和Oracle提倡的那樣,創建一個統一資料庫,不如提供一個能夠訪問不同資料庫的統一介面,這樣會更加實用一些。為實現這一目標,微軟在資料庫和微軟的OLE DB中提供了一種“橋”程序,這種程序能夠提供對資料庫的連接。開發人員在使用ADO時,其實就是在使用OLE DB,不過OLE DB更加接近底層。ADO的一項屬性-遠程數據服務,支持“數據倉庫”ActiveX 組件以及高效的客戶端緩存。作為ActiveX的一部分,ADO也是COM組件的一部分。ADO是由早期的微軟數據介面——遠程數據對象RDO演化而來的。RDO同微軟的ODBC一同連接關係資料庫,不過不能連接非關係資料庫。
ADO向我們提供了一個熟悉的,高層的對OLE DB的Automation封裝介面。對那些熟悉RDO的程序員來說,你可以把OLE DB比作是ODBC驅動程序。如同RDO對象是ODBC驅動程序介面一樣,ADO對象是OLE DB的介面;如同不同的資料庫系統需要它們自己的ODBC驅動程序一樣,不同的數據源要求它們自己的OLE DB提供者(OLE DB provider)。但微軟正積極推廣該技術,並打算用OLE DB取代ODBC。
ADO向VB程序員提供了很多好處。包括易於使用,熟悉的界面,高速度以及較低的內存佔用(已實現ADO2.0的Msado15.dll需要佔用342K內存,比RDO的Msrdo20.dll的368K略小,大約是DAO3.5的Dao350.dll所佔內存的60%)。同傳統的數據對象層次(DAO和RDO)不同,ADO可以獨立創建。因此你可以只創建一個"Connection"對象,但是可以有多個,獨立的"Recordset"對象來使用它。ADO針對客戶/伺服器以及WEB應用程序作了優化。
• ADO 是一項微軟的技術
• ADO 指 ActiveX 數據對象(ActiveX Data Objects)
• ADO 是一個微軟的 Active-X 組件
• ADO 會隨微軟的 IIS 被自動安裝
• ADO 是一個訪問資料庫中數據的編程介面
1.創建一個到資料庫的 ADO 連接。
2.打開資料庫連接。
3.創建 ADO 記錄集。
4.從記錄集提取您需要的數據。
5.關閉記錄集。
6.關閉連接。
對象 | 說明 |
Command | Command 對象定義了將對數據源執行的指定命令。 |
Connection | 代表打開的、與數據源的連接。 |
DataControl (RDS) | 綁定Recordset到一個或多個控制項上,以便在 Web 頁上顯示數據。 |
DataFactory (RDS Server) | 實現對客戶端應用程序的指定數據源進行讀/寫數據訪問的方法。 |
DataSpace (RDS) | 創建客戶端代理以便自定義位於中間層的業務對象。 |
Error | 包含與單個操作(涉及提供者)有關的數據訪問錯誤的詳細信息。 |
Field | 代表使用普通數據類型的數據的列。 |
Parameter | 參數化查詢或存儲過程的 Command 對象相關聯的參數或自變數。 |
Property | 代表由提供者定義的 ADO 對象的動態特性。 |
RecordSet | 代表來自基本表或命令執行結果的記錄的全集。 |
ADO庫包含三個基本介面:_ConnectionPtr介面、_CommandPtr介面和_RecordsetPtr介面。
1、_ConnectionPtr介面
返回一個記錄集或一個空指針。通常使用它來創建一個數據連接或執行一條不返回任何結果的SQL語句,如一個存儲過程。
2、_CommandPtr介面
返回一個記錄集。它提供了一種簡單的方法來執行返回記錄集的存儲過程和SQL語句。
3、_RecordsetPtr介面
是一個記錄集對象。與以上兩種對象相比,它對記錄集提供了更多的控制功能,如記錄鎖定,游標控制等。
4、介面間的區別與聯繫
使用_ConnectionPtr介面返回一個記錄集不是一個好的使用方法。對於要返回記錄的操作通常用_RecordsetPtr來實現。而用_ConnectionPtr操作時要想得到記錄條數得遍歷所有記錄,而用_RecordsetPtr時不需要。
在使用_CommandPtr介面時,你可以利用全局_ConnectionPtr介面,也可以在_CommandPtr介面里直接使用連接串。如果你只執行一次或幾次數據訪問操作,後者是比較好的選擇。但如果你要頻繁訪問資料庫,並要返回很多記錄集,那麼,你應該使用全局_ConnectionPtr介面創建一個數據連接,然後使用_CommandPtr介面執行存儲過程和SQL語句。
同_CommandPtr介面一樣,_RecordsetPtr介面不一定要使用一個已經創建的數據連接,可以用一個連接串代替連接指針賦給_RecordsetPtr的connection成員變數,讓它自己創建數據連接。如果你要使用多個記錄集,最好的方法是同Command對象一樣使用已經創建了數據連接的全局_ConnectionPtr介面,然後使用_RecordsetPtr執行存儲過程和SQL語句。
ActiveX 數據對象 (ADO) 是添加到 Microsoft Active Server Pages (ASP) 的一套高級別介面,有利於伺服器端與資料庫的連接。ADO 與低級別介面 (OLE DB) 一起使用則有利於 Microsoft Universal Data Access 策略。ADO 2.0 版可生成 Visual Studio Analyzer 事件。可使用這些事件跟蹤分散式應用程序中的 ADO 交互。
ADO 生成的 Visual Studio Analyzer 事件
事件 事件描述 事件數據
事件 | 事件描述 | 事件數據 |
ConnectionClose | 指示 ADO 要與 OLE DB 數據源斷開連接。 | 無。 |
ConnectionOpen | 指示 ADO 正在連接到 OLE DB 數據源。 | 如果客戶端提供,則為連接到數據源所用的連接字元串。 |
Find | 指示 ADO 客戶端已調用 ADO Recordset.Find 函數。 | “查找”操作的判據;根據該判據匹配記錄。 |
GetRows | 指示 ADO 客戶端已調用 ADO Recordset.GetRows 函數。 | 提取的行數。 |
QueryResult | 指示資料庫已返迴響應查詢的結果集。 | 無。 |
QuerySend | 指示 ADO 正在執行命令。該事件可由下列函數觸發: Connection.ExecuteCommand.ExecuteConnection.<存儲過程名> Recordset.Open | 構成查詢的 SQL 語句。 |
RecordsetOpen | 指示 ADO 正在打開遠程伺服器上的記錄集。僅適用於三層方案。 | 打開記錄集的源(通常為行返回的命令文本)。 |
Sort | 指示 ADO 準備篩選或對數據排序。 | 排序或篩選應用於記錄集數據的判據。 |
Transaction Rollback | 指示 ADO 要中止當前本地事務。 | 返回真或假。如果為真,則保持中止,即該事務中止后緊跟著開始另一事務。如果為假,則不保持中止。 |
TransactionCommit | 指示 ADO 正在提交 OLE DB 提供程序上的本地事務。 | 返回真或假。如果為真,則保留提交,即該事務提交后緊跟著開始另一事務。如果為假,則不保留提交。 |
TransactionStart | 指示 ADO 正在開始 OLE DB 提供程序上的本地事務。 | ADO 開始事務所基於的隔離級別。隔離級別指示可看到其他事務所做更改的哪一級別。 |
UpdateBatch | 指示 ADO 正在向提供程序發送更新批處理。僅適用於三層方案。 | 如果有,為 ADO 將更新發送到的遠程伺服器名。 |
以前的對象模型,如DAO和RDO是層次型的。也就是說一個較低的數據對象如Recordset是幾個較高層次的對象,如Environment和QueryDef,的子對象。在創建一個QueryDef對象的實例之前,你不能創建DAO Recordset對象的實例。但ADO卻不同,它定義了一組平面型頂級對象.。
最重要的三個ADO對象是Connection, Recordset和Command. 本文將主要介紹Connection和Recordset這兩個對象。每個Connection的屬性定義了與數據源的連接。Recordset對象接收來自數據源的數據。Recordset可以與Connection一起起使用,先建立一個連接,然後獲取數據。儘管如此,Recordset也可以被單獨創建,其Connection參數可以在Open屬性定義。
對於ADO1.5以前包括1.5的版本來說,從功著你可以通過這兩種方法解決同樣的問題;它不是指存在重命名的或者優化的功能相同的對象。因此,移植到ADO不是一個簡單的事情。從另一方面來說,一旦你熟練掌握了RDO或DAO技術的話,學習ADO是件相當容易的事情.
ADO 2.0的新特性包括事件處理,記錄集的延續,分層目錄結構指針和數據成形,分散式事務處理,多維數據,遠程數據服務(RDS),以及對C++和Java的支持的增強。在鑽研一些Visual Basic代碼的時候將會見到所有的這些特性。當使用Visual J++時,我將舉例說明新的Windows Foundation Classes(WFC)是如何支持ADO的.ADO的最讓人激動的是在Visual Studio 6.0中的任何開發工具中你都可以找到對它的充分的支持.
連接數據源 (Connection),可選擇開始事務。
可選擇創建表示 SQL 命令的對象 (Command)。
可選擇指定列、表以及 SQL 命令中的值作為變數參數 (Parameter)。
執行命令(Command、Connection 或 Recordset)。
如果命令以行返回,將行存儲在存儲對象中 (Recordset)。
可選擇創建存儲對象的視圖以便進行排序、篩選和定位數據 (Recordset)。
編輯數據。可以添加、刪除或更改行、列 (Recordset)。
在適當情況下,可以使用存儲對象中的變更對數據源進行更新 (Recordset)。
在使用事務之後,可以接受或拒絕在事務中所做的更改。結束事務 (Connection)。
一旦安裝了ADO,在VB的工程->引用對話框中你就可以看到象下圖所示的東西了:
選擇 "ActiveX Data Objects 1.5 Library" (ADODB).在其下的 "ADO Recordset 1.5 Library"是一個客戶端的版本(ADOR),它定義了有聚的數據訪問對象。ADOR 對於客戶端的數據訪問來說是足夠的了,因為你不需要Connection對象來建立與遠程數據源的聯繫。
如果你想要訪問更多的外部數據源,你需要安裝這些外部數據源自己的OLE DB Provider,就象你需要為新的資料庫系統安裝新的ODBC驅動程序一樣。如果該外部數據源沒有自己的OLE DB Provider,你就得使用OLE DB SDK來自己為這個外部數據源創建一個OLE DB Provider了。這已不是本文討論的範圍了。
示例
下面的示例代碼以Northwinds資料庫作為遠程數據源,然後用ADO來訪問它。首先在控制面板中打開“32位數據源”,單擊“添加”按鈕。在彈出的對話框中選擇 "Microsoft Access Driver (*.mdb)" 作為數據源驅動程序。
然後按下圖所示,在對話框中填寫下面的內容
選擇資料庫Northwinds所在路徑。單擊完成,退出ODBC設備管理器。
啟動一個新的VB工程,在窗體的Load事件中輸入下面的代碼:
Private Sub Form_Load()
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
'Set Connection properties
cn.ConnectionTimeout = 30
cn.Open
If cn.State = adStateOpen Then _
MsgBox "Connection to NorthWind Successful!"
cn.Close
End Sub
按F5運行程序,看看,一個消息框彈出來告訴你連接成功了。請注意,這裡我特別註明了是ADODB.Connection,而不是ADOR.Connection,這樣做是為了將二者區分開(如果你引用了ADODB和ADOR的話,這樣做很有必要)。連接字元串看上去同RDO的連接字元串差不多。事實上,二者確實差不多。
如果我們要訪問一個SQL server資料庫,你的Connection代碼看上去應象下面所示:
'設置連接屬性cn.Provider = "MSDASQL"
cn.Open
"Provider"屬性指向SQL Server的OLE DB Provider.
回到我們的示常式序,讓我們創建一個Recordset對象來訪問“Orders”表,並從該表的"ShipCountry"欄位中產生頭十個不重複的國家名。修改窗體Load事件中的代碼,讓它看上去象下面這樣。
Private Sub Form_Load()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sSQL As String
Dim sOut As String
Dim Count As Integer
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Set properties of the Connection.
cn.ConnectionString = "DSN=RDC Nwind;UID=;PWD=;"
cn.ConnectionTimeout = 30
cn.Open
If cn.State = adStateOpen Then _
MsgBox "Connection to NorthWind Successful!"
sSQL = "SELECT DISTINCT Orders.ShipCountry FROM Orders"
Set rs = cn.Execute(sSQL)
'Enumerate the recordset
sOut = ""
For Count = 1 To 10
sOut = sOut & rs("ShipCountry") & vbCrLf
rs.MoveNext
Next Count
MsgBox sOut, vbExclamation, "ADO Results"
cn.Close
End Sub
運行程序后,你會看到如下圖所示的消息框。
不幸的是你需要創建一個獨立的Recordset對象,該對象擁有自己的Connection屬性,就象下面的代碼所示:
Private Sub Form_Load()
Dim rs As ADODB.Recordset
Dim sSQL As String
Dim sOut As String
Dim Count As Integer
Set rs = New ADODB.Recordset
sSQL = "SELECT DISTINCT Orders.ShipCountry FROM Orders"
rs.Open sSQL, "DSN=RDC Nwind;UID=;PWD=;", adOpenDynamic
'Report Recordset Connection information
MsgBox rs.ActiveConnection, , "Connection Info"
'Enumerate the recordset
sOut = ""
For Count = 1 To 10
sOut = sOut & rs("ShipCountry") & vbCrLf
rs.MoveNext
Next Count
MsgBox sOut, vbExclamation, "ADO Results"
rs.Close
End Sub
上面代碼返回的結果同前例一樣,但是本代碼中的Recordset是獨立的。這一點是DAO和RDO做不到的。Recordset對象的Open方法打開一個代表從SQL查詢返回的記錄的游標。雖然你可以用Connection對象同遠程數據源建立連接,但請記住,在這種情況下,Connection對象和Recordset對象是平行的關係。
總結
本文僅向你介紹了ADO強大的功能的冰山一角。微軟承諾,在將來ADO將會取代DAO和RDO。所以現在你應該考慮將你的數據訪問代碼投向ADO的懷抱。因為ADO的語法同現有的語法差不多。也許微軟或第三方會在將來開發出轉換嚮導來簡化這一轉換過程。從現在起,你就開發純ADO代碼的程序。你也可以繼續使用DAO或RDO代碼來開發你的程序,但落伍的感覺總是不好的。
ADO(ActiveX Data Objects)是基於組件的資料庫編程介面,它是一個和編程語言無關的COM組件系統。本文主要介紹用ADO編程所需要注意的技巧和在VC下進行ADO編程的模式,並對C++Extensions進行了簡單的討論,希望對ADO開發人員有一定的幫助作用。因為ADO是一個和編程語言無關的COM組件系統,所以這裡討論的要點適用於所有的編程語言和編程環境,比如:VB、VBScript、VC、Java等等。
編程技巧
1.顯式定義對象類型
實際上,這條準則不僅適用於ADO編程,也適用於其他的與COM對象相關的編程。因為如果一開始就定義變數類型,則編譯器在編譯的時候就可以知道變數的類型,此時編譯器實際上是採用vtable偏移的方式來得到具體的COM對象包含的方法的地址(這一點和C++中虛函數的地址獲取方式類似);但如果一開始不指定變數類型的話,比如簡單地採用如下的語句:
DIM myCon as Object
或者是:
DIM myCon
這樣,編譯器在編譯的時候就不能得到變數的類型,而只能在運行的時候動態地得到方法的信息(通過使用介面IDispatch的Invoke方法來實現),如此為了得到方法的地址和相關的變數情況就需要在內部進行兩次調用,無疑會降低程序的運行速度。
2.綁定列到欄位對象
在程序開始時就建立對欄位對象的引用,可以避免在每次得到記錄后,再在Recordset::Fields中進行查找而增加系統的開銷。
例如,可以採用如下所示的代碼:
Private Sub TblBrowse_Click()
Dim fld1 As ADODB.Field
Dim fld2 As ADODB.Field
Dim rs As ADODB.Recordset
set rs=g_cn.execute(...)
'g_cn為全局對象adodb.connection
Set fld1 = rs.Fields(“id”) '數據表的欄位
Set fld2 = rs.Fields(“name”) ’數據表的欄位
If rs.BOF = False Then
While rs.BOF = False
Debug.Print fld1.Value
Debug.Print fld2.Value
rs.MoveNext
Wend
End If
rs.Close
End Sub
3.好的數據更新方式
儘管採用Recordset對象來更新數據是非常方便的,但是它的開銷也大,通過數據源對象返回的查詢集不僅包含了數據,而且也包含了元數據(metadata),在有些時候元數據可能比數據本身還要大,所以最好採用SQL語句來更新數據。還有要使用存儲過程而不是單一的SQL語句來獲取信息。因為存儲過程是在伺服器端執行的,只把結果返回到客戶端,這樣一方面可以降低網路進行數據交互的開銷,另一方面使系統更加容易維護,並且能保持數據的一致性。
4.操作單條SELECT語句
在使用游標時,最好使用集合的方法對單條的SELECT語句進行操作。Recordset::get_Collect方法和Recordset::put_Collect方法是Recordset 對象的快捷方式,可以快速地得到一個欄位的值而不需要獲得關於一個欄位的引用。例如,可以採用如下代碼:
Sub Collect()
Dim rs As New Recordset
rs.ActiveConnection = “...”
rs.Source=“一條SQL查詢語句”
rs.Open
Debug.Print rs.Collect(0),rs.Collect(1),rs.Collect(2)
Debug.Print rs!au_id, rs!au_fname, rs!au_lname
End Sub
5.查詢所需要的數據
儘管很多開發人員都習慣採用“SELECT * FROM TBL”的模式進行查詢,但是為了提高系統的效率,如果只需要其中某幾個欄位的值,最好把這幾個欄位直接寫出來,同時需要限定返回記錄集的範圍(通過WHERE子句進行限定)。
6.正確選擇游標參數
如果只需要按順序讀取記錄並且不需要滾動和更新記錄,最好使用伺服器端游標(adUseServer)、僅向前游標(adOpenForwardOnly)和讀加鎖(adLockReadOnly),這樣可以獲得最好的性能。如果需要滾動記錄,採用客戶端游標(adUseServer)會比採用伺服器端游標所得到的性能要好,因為ADO系統默認是採用伺服器端游標類型。當然如果數據集合相當大,採用伺服器端游標的性能會好一些。同時需要注意:如果採用客戶端游標,最好只採用讀加鎖(adLockReadOnly)的鎖類型,因為如果需要更新數據,客戶端游標引擎需要得到額外的信息(元數據),而獲取這個信息的代價是非常昂貴的。
7.調整CacheSize屬性
ADO使用記錄集對象的CacheSize屬性來決定提取和緩存的記錄的數目,當在緩存的範圍內瀏覽數據時,ADO就只從緩存中提取數據。當要瀏覽的數據超出緩存範圍的時候,ADO就釋放當前緩存,提取下一些記錄(提取的數目為CacheSize所指定的大小),所以必須根據具體的應用程序的情況,來設定CacheSize的大小,保證得到最佳的性能。
8.定義Command參數
在許多數據源中,得到參數信息和執行命令的代價幾乎是一樣的,所以最好自己在程序中定義好Command參數(也就是說要定義好參數的名稱、類型和方向信息),避免一些從數據提供者(Provider)那裡獲取信息的操作。
9.使用原始的提供者
MDAC對許多數據源提供了原始的數據提供者,比如SQL Server、Oracle和Access資料庫,這樣就不需要再通過ODBC來獲取數據(也就是說不需要再通過ODBC驅動這一層),這樣的好處是能更快地得到數據,並且能降低磁碟和內存的開銷。
10.斷開相關對象連接
如果使用客戶端游標,就要斷開Connection連接。ADO有一個特徵是當使用客戶端游標操作Recordset記錄集的時候,不需要和伺服器保持聯繫。所以可以充分利用這個特性降低伺服器端的開銷(伺服器就不需要維護這些連接了)。當操作完記錄集需要更新時,可以重新和資料庫進行連接來更新數據。為了創建一個可以斷開連接的記錄集,同時需要使用靜態游標(adOpenStatic)和批處理的加鎖模式(adLockBatchOptimistic)。下面是有關處理的VC代碼:
pRs.CreateInstance(__uuid(Recordset));
pRs->CursorLoction=adUseClient;
pRs->Open(strCmdText,strConnection,adOpenStatic,adLockBatchOptimistic,adCmdText);
pRs->PutRefActiveConnection(NULL);
//對記錄集對象pRs進行操作
//重新和資料庫建立連接
pRs->PutRefActiveConnectio(pCon);
//批量更新數據
pRs->UpdateBatch(adAffectAll);
需要注意的是:當執行批量更新時,必須自己處理數據衝突問題,因為更新數據時,其他用戶也可能同時正在對該數據進行操作。
11.不返回記錄的選項
如果不需要返回記錄,要使用adExecuteNoRecords選項。ADO 2.0包括一個新的執行選項稱為adExecuteNoRecords。當使用該選項的時候,ADO就不會創建記錄集對象,不設置任何游標屬性。數據提供者因為不需要認證集合的屬性而使性能得到優化。具體的例子如下:
con.Execute “insert into tbl values(fv1, fv2) ”, , adExecuteNoRecords
對僅有一條的執行語句採用Connection::Execute方法比使用Recordset::Open方法或者是Command::Execute方法的效果要好,因為ADO不保留任何命令狀態的信息,因此執行性能就有所改進。
12.使用正確的緩衝池
因為資料庫的打開和關閉非常消耗系統資源,因此,使用連接池對基於多層的應用的性能會有很大的提高。當使用MDAC的時候,開發人員本身並不需要考慮對資料庫連接的緩存,MDAC會自動處理它。連接池在兩個層次上提供支持:OLE DB sessions和ODBC連接。如果使用ADO,資料庫連接會自動被OLE DB session緩衝池所緩存;如果使用ODBC,可以利用在ODBC數據源管理中新的連接緩衝池選項對ODBC緩衝進行設置。
我們知道,在VB下進行基於ADO的編程相對比較簡單,只要通過reference載入了適當的類型庫后,就可以正常地調用ADO對象。但是對於VC下的基於ADO的資料庫開發就稍微複雜一些。VC中實現對ADO操作通常有三種方法:
#import方法;
利用MFC OLE的ClassWizard;
通過Windows API中COM相關的函數。
在這三種方法中,#import是最方便的方法,它允許產生一個類似VB的類結構,使程序開發變得很方便。下面分別介紹這三種方法。
1.#import方法
在#import方法中,需要提供所要包含的類型庫的路徑和名稱,VC能夠自動產生一個對GUIDs的定義,以及自動生成對ADO對象的封裝。對任何引用的類型庫,VC會在編譯的時候自動生成兩個文件:
頭文件(.tlh):包含了所列舉的類型和對類型庫中對象的定義;
實現文件(.tli):對類型庫對象模型中的方法產生封裝。
例如,在stdafx.h文件中增加對msado15.dd的
#import之後,VC會產生msado15.tlh和msado15.tli兩個文件。
#import能夠使用一個新的類_com_ptr_t,它也被稱為智能指針。智能指針能夠自動執行QuyerInterface、AddRef和Release函數。
下面的代碼演示了如何使用#import在應用中實現對ADO的操作:
#import “c:\program files\common files\system\ado\msado15.dll” \no_namespace
rename ( “EOF”, “adoEOF” )
重命名EOF是必要的,因為典型的VC應用都已經定義了EOF作為常數-1。
通常來說,操作一個自動化對象需要定義和初始化一個用來操作的變數。可以通過使用智能指針
(_com_ptr_t)的構造函數傳遞一個有效的CLSID或者是PROGID,也可以通過_com_ptr_t::CreateInstance()方法來定義對象。具體代碼如下所示:
_ConnectionPtr Conn1( __uuidof( Connection ) );
也可以採用下面的代碼實現同樣的功能:
_ConnectionPtr Conn1 = NULL; //定義對象
HRESULT hr = S_OK;
//創建實例
hr =Conn1.CreateInstance( __uuidof( Connection ) );
推薦採用第二種方式,因為用第一種方式不能返回一個失敗的HRESULT,所以也就不能判斷ADO連接對象是成功還是失敗,以及失敗的原因。注意這裡的__uuidof( Connection)中的Connection是在.tlh文件中定義的。通過把它傳遞給方法CreateInstance,就可以創建一個有效的ADOConnection對象。
需要注意的是#import的no_namespace屬性,它告訴編譯器該類在不在一個單獨的名字空間中。使用no_namespace意味著不需要在初始化變數時引用名字空間。當然如果在應用中需要導入多個類型庫時,最好不要使用no_namespace,以免引起名字衝突。
下面是一個簡單的採用了#import方法的基於ADO應用的示例代碼:
#include
#import rename(“EOF”, “adoEOF”)
void main()
{
HRESULT hr = S_OK;
//因為沒有在#import中指定no_namespace,所以必須採用ADODB::這樣的形式來定義變數類型
ADODB::_RecordsetPtr Rs1 = NULL;
//通過ODBC建立ADO連接
_bstr_t Connect( “DSN=AdoDemo;UID=sa;PWD=;” );
_bstr_t Source ( “SELECT * FROM Authors” );
CoInitialize();
//初始化Rs1對象
hr = Rs1.CreateInstance( __uuidof( ADODB::Recordset ) );
//省略對返回值hr的判斷
Rs1->Open( Source,
Aonnect,
ADODB::adOpenForwardOnly,
ADODB::adLockReadOnly,
-1 );
//此處可以添加對記錄集Rs1進行操作的代碼
Rs1->Close();
Rs1 = NULL;
::MessageBox( NULL,“Success!”,“”,MB_OK );
CoUninitialize();
}
2.用MFC OLE創建ADO應用
MFC OLE同樣能夠封裝(wrapper)一個類型庫,但是與#import不同,它不能從類型庫中產生枚舉類型。MFC類CString和COleVariant隱藏了BSTRS和Variants的細節。由MFC OLE產生的類都繼承了類ColeDispatchDriver,由ADO產生的失敗的HRESULTS被封裝在類ColeDispatchException中。
用MFC OLE ClassWizard創建ADO應用的步驟如下:
從Tools菜單中,選擇Options選項中的Directories tab條目,在Show Directories中的Library Files中增加路徑C:\program files\common files\system\ado,設置包含ADO類型庫的路徑。
從View菜單中,激活ClassWizard,點擊Add Class按鈕並選擇“From A Type Library...”選項,然後在Type Library dialog box對話框中,從C:\program files\common files\system\ado中選擇文件msado15.dll,在Confirm Classes對話框中,選擇所有列出的類並按OK按鈕退出ClassWizard。這樣,ClassWizard便生成了兩個文件msado15.h和msado15.cpp。
下面是實現ADO應用的示例代碼:
//初始化COM對象
AfxOleInit();
...
//定義數據集對象
_Recordset Rs1;
COleException e;
COleVariant Connect( “DSN=AdoDemo;UID=sa;PWD=;” );
COleVariant Source ( “SELECT * FROM Authors” );
//創建數據集對象
Rs1.CreateDispatch(“ADODB.Recordset.2.0”,&e );
Rs1.Open( (VARIANT) Source,
(VARIANT) Connect,
0, 1, -1 );
//此處可以添加對結果集Rs1進行處理的代碼
Rs1.Close();
Rs1.ReleaseDispatch();
AfxMessageBox(“Success!”);
3.用COM API創建ADO工程
#import和MFC OLE都圍繞著一個給定的自動化對象產生了一個封裝類,它們分別繼承自_com_ptr_t和ColeDispatchDriver。其實也可以通過使用Windows API函數直接初始化ADO對象。為了直接使用ADO和COM對象,需要添加兩個頭文件adoid.h和adoint.h,這兩個頭文件定義了CLSIDs、介面定義和操作ADO類型庫所需要的枚舉類型。此外,還需要增加頭文件INITGUID.H。
為了能夠編譯用COM API創建的ADO工程文件,還需要在機器中安裝OLE DB SDK或者是MSDASDK工具。下面是利用API創建ADO的簡單的示例代碼:
#include
#include
#include “adoid.h” // ADO的GUID's
#include “adoint.h” // ADO的類、枚舉等等
void main()
{
HRESULT hr = S_OK;
// ADORecordset 是在adoint.h中定義的
ADORecordset*Rs1 = NULL;
VARIANT Source;
VARIANT Connect;
VariantInit( &Source );
VariantInit( &Connect );
Source.vt = VT_BSTR;
Source.bstrVal = ::SysAllocString( L“SELECT * FROM Authors”);
Connect.vt = VT_BSTR;
Connect.bstrVal = ::SysAllocString( L“DSN=AdoDemo;UID=sa;PWD=;” );
hr = CoCreateInstance( CLSID_CADORecordset,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADORecordset,
(LPVOID *) &Rs1 );
if( SUCCEEDED( hr ) ) hr = Rs1->Open
(Source,
Connect,
adOpenForwardOnly,
adLockReadOnly,
-1 );
//對記錄集Rs1進行處理
if( SUCCEEDED( hr ) ) hr = Rs1->Close();
if( SUCCEEDED( hr ) ) { Rs1->Release(); Rs1 = NULL; }
if( SUCCEEDED( hr ) ) ::MessageBox( NULL, “Success!”, “”, MB_OK );
}
C++ Extensions
如果用C++進行ADO應用程序開發,應該使用ADO C++ Extensions。我們知道,用VB或者VBScript來操作ADO是非常方便的,但是如果使用C++或者是Java,就必須要處理類似Variants這樣的數據結構以實現和C++數據結構的轉換,而這種處理無疑是所有C++開發人員都很頭疼的事情。但如果使用C++ Extensions的話,ADO就不需要從數據提供者處得到列信息,而是在設計時刻使用開發人員提供的列信息。以下是一個簡單的示例:
//創建和具體記錄相對應的類
class CAuthor : public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs1)
ADO_VARIABLE_LENGTH_ENTRY4(1,
adVarChar, m_szau_id, sizeof(m_szau_id), FALSE)
ADO_VARIABLE_LENGTH_ENTRY4(2,
adVarChar,m_szau_fname,sizeof(m_szau_fname), FALSE)
ADO_VARIABLE_LENGTH_ENTRY4(3,
adVarChar,m_szau_lname,sizeof(m_szau_lname), FALSE)
END_ADO_BINDING()
protected:
char m_szau_id【12】;
char m_szau_fname【21】;
char m_szau_lname【41】;
};
void FetchAuthorData()
{
CAuthor author;
//記錄集對象
_RecordsetPtr pRs;
IADORecordBinding *piAdoRecordBinding;
//獲取COM對象介面指針
pRs.CreateInstance(__uuidof(Recordset));
//得到需要的記錄集
pRs->Open(“select au_id,au_fname,au_lname from Employees”,“Provider=SQLOLEDB;Data Source=sureshk1;Database=pubs;User Id=sa;Password=;”,
adOpenForwardOnly,
adLockReadOnly,
adCmdText);
//查詢介面IADORecordBinding
pRs->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&piAdoRecordBinding);
//綁定對象
piAdoRecordBinding->BindToRecordset(&author);
//得到記錄中的相關內容
while (VARIANT_FALSE == pRs->EOF) {
printf(“%s %s %s”, author.m_szau_id,
author.m_szau_fname, author.m_szau_lname);
pRs->MoveNext();
}
//釋放對象