簡介
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的Ms
ado15.dll需要占用342K記憶體,比RDO的Msrdo20.dll的368K略小,大約是
DAO3.5的Dao350.dll所占記憶體的60%)。同傳統的
數據對象層次(DAO和RDO)不同,ADO可以獨立創建。因此你可以只創建一個"Connection"對象,但是可以有多個,獨立的"Recordset"對象來使用它。ADO針對客戶/伺服器以及WEB應用程式作了最佳化。
定義
從 ASP 頁面訪問資料庫
從一個 ASP 頁面內部訪問資料庫的通常的方法是:
創建一個到資料庫的 ADO 連線
打開資料庫連線
創建 ADO 記錄集
從記錄集提取您需要的數據
關閉記錄集
關閉連線
對象總結
對象
| 說明
|
Command
| Command 對象定義了將對數據源執行的指定命令。
|
Connection
| 代表打開的、與數據源的連線。
|
| 綁定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語句。
ADO 事件
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.Execute Command.Execute Connection.<存儲過程名> Recordset.Open
| 構成查詢的 SQL 語句。 |
RecordsetOpen | 指示 ADO 正在打開遠程伺服器上的記錄集。僅適用於三層方案。 | 打開記錄集的源(通常為行返回的命令文本)。 |
Sort | 指示 ADO 準備篩選或對數據排序。 | 排序或篩選套用於記錄集數據的判據。 |
Transaction Rollback | 指示 ADO 要中止當前本地事務。 | 返回真或假。如果為真,則保持中止,即該事務中止後緊跟著開始另一事務。如果為假,則不保持中止。 |
TransactionCommit | 指示 ADO 正在提交 OLE DB 提供程式上的本地事務。 | 返回真或假。如果為真,則保留提交,即該事務提交後緊跟著開始另一事務。如果為假,則不保留提交。 |
TransactionStart | 指示 ADO 正在開始 OLE DB 提供程式上的本地事務。 | ADO 開始事務所基於的隔離級別。隔離級別指示可看到其他事務所做更改的哪一級別。 |
UpdateBatch | 指示 ADO 正在向提供程式傳送更新批處理。僅適用於三層方案。 | 如果有,為 ADO 將更新傳送到的遠程伺服器名。 |
組織形式
以前的對象模型,如DAO和RDO是層次型的。也就是說一個較低的
數據對象如Reco
rdset是幾個較高層次的對象,如Environment和QueryDef,的
子對象。在創建一個QueryDef對象的實例之前,你不能創建DAO
Recordset對象的實例。但ADO卻不同,它定義了一組平面型頂級對象.。
最重要的三個ADO對象是Connection, Recordset和Command. 本文將主要介紹Connection和Recordset這兩個對象。每個Connection的屬性定義了與
數據源的連線。Recordset對象接收來自數據源的數據。Recordset可以與Connection一起起使用,先建立一個連線,然後獲取數據。儘管如此,Recordset也可以被單獨創建,其Connection參數可以在Open屬性定義。
ADO 2.0特點
對於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.ConnectionString = "DSN=RDC Nwind;UID=;PWD=;"
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.ConnectionString = "driver={SQL Server};" & "server=prod1;uid=bg;pwd=;
database=main"
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.綁定列到欄位對象
在程式開始時就建立對欄位對象的引用,可以避免在每次得到記錄後,再在Reco
rdset::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有一個特徵是當使用客戶端游標操作Reco
rdset
記錄集的時候,不需要和伺服器保持聯繫。所以可以充分利用這個特性降低伺服器端的開銷(伺服器就不需要維護這些連線了)。當操作完記錄集需要更新時,可以重新和資料庫進行連線來更新數據。為了創建一個可以下線的記錄集,同時需要使用
靜態游標(adOpenStatic)和批處理的加鎖模式(adLockBatchOptimistic)。下面是有關處理的VC代碼:
pRs.CreateInstance(__uuid(Recordset));
pRs->CursorLoction=adUseClient;
pRs->Open(strCmdText,strConnection,adOpenStatic,adLockBatchOptimistic,adCmdText);
pRs->PutRefActiveConnection(NULL);
//重新和資料庫建立連線
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->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;
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.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 );
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();
}
//釋放對象