ADO程式設計應用 (轉)

a400發表於2007-08-13
ADO程式設計應用 (轉)[@more@]

ADO應用

ADO( Data s)是基於的程式設計介面,它是一個和程式語言無關的COM元件。本文主要介紹用ADO程式設計所需要注意的技巧和在VC下進行ADO程式設計的,並對C++Extensions進行了簡單的討論,希望對ADO開發人員有一定的幫助作用。因為ADO是一個和程式語言無關的COM元件系統,所以這裡討論的要點適用於所有的程式語言和程式設計環境,比如:VB、、VC、等等。
程式設計技巧
1.顯式定義型別
實際上,這條準則不僅適用於ADO程式設計,也適用於其他的與COM物件相關的程式設計。因為如果一開始就定義變數型別,則在編譯的時候就可以知道變數的型別,此時編譯器實際上是採用vtable偏移的方式來得到具體的COM物件包含的方法的地址(這一點和C++中虛的地址獲取方式類似);但如果一開始不指定變數型別的話,比如簡單地採用如下的語句:
DIM myCon as Object
或者是:
DIM myCon
這樣,編譯器在編譯的時候就不能得到變數的型別,而只能在執行的時候動態地得到方法的資訊(透過使用介面IDispatch的Invoke方法來實現),如此為了得到方法的地址和相關的變數情況就需要在內部進行兩次,無疑會降低的執行速度。
2.繫結列到具體的欄位物件
在程式開始時就建立對欄位物件的引用,可以避免在每次得到記錄後,再在Recordset::Fields中進行查詢而增加系統的開銷。
例如,可以採用如下所示的程式碼:
Private Sub TblBrowse_Click()
Dim fld1 As ADO.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
De.Print fld1.Value
Debug.Print fld2.Value
rs.MoveNext
Wend
End If
rs.Close
End Sub
3.用語句和過程進行資料
儘管採用Recordset物件來更新資料是非常方便的,但是它的開銷也大,透過資料來源物件返回的查詢集不僅包含了資料,而且也包含了後設資料(metadata),在有些時候後設資料可能比資料本身還要大,所以最好採用SQL語句來更新資料。還有要使用儲存過程而不是單一的SQL語句來獲取資訊。因為儲存過程是在端的,只把結果返回到客戶端,這樣一方面可以降低進行資料互動的開銷,另一方面使系統更加容易維護,並且能保持資料的一致性。
4.使用集合操作單條的語句
在使用遊標時,最好使用集合的方法對單條的SELECT語句進行操作。Recordset::get_Collect方法和Recordset::put_Collect方法是Recordset 物件的快捷方式,可以地得到一個欄位的值而不需要獲得關於一個欄位的引用。例如,可以採用如下程式碼:
Sub Collect()
Dim rs As New Recordset
rs.ActiveConnection = “...”
rs.=“一條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引數(也就是說要定義好引數的名稱、型別和方向資訊),避免一些從資料提供者(Provr)那裡獲取資訊的操作。
9.使用原始的OLE DB提供者
MDAC對許多資料來源提供了原始的資料提供者,比如、和Access資料庫,這樣就不需要再透過ODBC來獲取資料(也就是說不需要再透過ODBC這一層),這樣的好處是能更快地得到資料,並且能降低和的開銷。
10.斷開Connection連線
如果使用客戶端遊標,就要斷開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選項
如果不需要返回記錄,要使用adExecuteNoRecords選項。ADO 2.0包括一個新的執行選項稱為adExecuteNoRecords。當使用該選項的時候,ADO就不會建立記錄集物件,不設定任何遊標屬性。資料提供者因為不需要集合的屬性而使效能得到。具體的例子如下:
con.Execute “insert into tbl values(fv1, fv2) ”, , adExecuteNoRecords
對僅有一條的執行語句採用Connection::Execute方法比使用Recordset::Open方法或者是Command::Execute方法的效果要好,因為ADO不保留任何命令狀態的資訊,因此執行效能就有所改進。
12.使用session/connection緩衝池
因為資料庫的開啟和關閉非常消耗系統資源,因此,使用連線池對基於多層的應用的效能會有很大的提高。當使用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;
?透過 中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 filescommon filessystemadomsado15.dll” o_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產生的類都繼承了類ColeDispatch,由ADO產生的失敗的HRESULTS被封裝在類ColeDispatchException中。
用MFC OLE ClassWizard建立ADO應用的步驟如下:
?從Tools選單中,選擇Options選項中的Directories tab條目,在Show Directories中的Library Files中增加路徑C:program filescommon filessystemado,設定包含ADO型別庫的路徑。
?從View選單中,啟用ClassWizard,點選Add Class按鈕並選擇“From A Type Library...”選項,然後在Type Library dialog box對話方塊中,從C:program filescommon filessystemado中選擇檔案msado15.dll,在ConfiClasses對話方塊中,選擇所有列出的類並按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.用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;Pass=;”,
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();
}
//釋放物件
piAdoRecordBinding->Release();
}

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10790690/viewspace-953035/,如需轉載,請註明出處,否則將追究法律責任。

相關文章