Visual C++ ADO資料庫程式設計入門

kaiyinkaiyin發表於2008-04-21
ado[@more@]摘要 本文簡要介紹了在VC++ 6.0中使用 ADO進行客戶端資料庫程式設計的基本步驟,以及常見問題的解決方法,可供入門級的參考之用。

  關鍵字 ADO VC++ 資料庫程式設計

  ADO 是目前在Windows環境中比較流行的客戶端資料庫程式設計技術。ADO是建立在OLE DB底層技術之上的高階程式設計介面,因而它兼具有強大的資料處理功能(處理各種不同型別的資料來源、分散式的資料處理等等)和極其簡單、易用的程式設計介面,因而得到了廣泛的應用。而且按微軟公司的意圖,OLE DB和ADO將逐步取代 ODBC和DAO。現在介紹ADO各種應用的文章和書籍有很多,本文著重站在初學者的角度,簡要探討一下在VC++中使用ADO程式設計時的一些問題。我們希望閱讀本文之前,您對ADO技術的基本原理有一些瞭解。

  一、在VC++中使用ADO程式設計

  ADO實際上就是由一組Automation物件構成的元件,因此可以象使用其它任何Automation物件一樣使用ADO。ADO中最重要的物件有三個:Connection、Command和Recordset,它們分別表示連線物件、命令物件和記錄集物件。如果您熟悉使用MFC中的ODBC類(CDatabase、CRecordset)程式設計,那麼學習ADO程式設計就十分容易了。

  使用ADO程式設計時可以採用以下三種方法之一:

  1、使用預處理指令#import

#import "C:Program FilesCommon FilesSystemADOmsado15.dll"
no_namespace rename("EOF", "EndOfFile")

  但要注意不能放在stdAfx.h檔案的開頭,而應該放在所有include指令的後面。否則在編譯時會出錯。
程式在編譯過程中,VC++會讀出msado15.dll中的型別庫資訊,自動產生兩個該型別庫的標頭檔案和實現檔案msado15.tlh和msado15.tli(在您的Debug或Release目錄下)。在這兩個檔案裡定義了ADO的所有物件和方法,以及一些列舉型的常量等。我們的程式只要直接呼叫這些方法就行了,與使用MFC中的COleDispatchDriver類呼叫Automation物件十分類似。

  2、使用MFC中的CIDispatchDriver

  就是透過讀取msado15.dll中的型別庫資訊,建立一個COleDispatchDriver類的派生類,然後透過它呼叫ADO物件。

  3、直接用COM提供的API

  如使用如下程式碼:

CLSID clsid;
HRESULT hr = ::CLSIDFromProgID(L"ADODB.Connection", &clsid);
if(FAILED(hr))
{...}
::CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)
&pDispatch);
if(FAILED(hr))
{...}


  以上三種方法,第一和第二種類似,可能第一種好用一些,第三種程式設計可能最麻煩。但可能第三種方法也是效率最高的,程式的尺寸也最小,並且對ADO的控制能力也最強。

  據微軟資料介紹,第一種方法不支援方法呼叫中的預設引數,當然第二種方法也是這樣,但第三種就不是這樣了。採用第三種方法的水平也最高。當你需要繞過ADO而直接呼叫OLE DB底層的方法時,就一定要使用第三種方法了。

  ADO程式設計的關鍵,就是熟練地運用ADO提供的各種物件(object)、方法(method)、屬性(property)和容器(collection)。另外,如果是在MS SQL或Oracle等大型資料庫上程式設計,還要能熟練使用SQL語言。

二、使用#import方法的程式設計步驟  這裡建議您使用#import的方法,因為它易學、易用,程式碼也比較簡潔。

  1、 新增#import指令

  開啟stdafx.h檔案,將下列內容新增到所有的include指令之後:

#include //Include support for VC++ Extensions
#import "C:Program FilesCommon FilesSystemADOmsado15.dll"
no_namespace rename("EOF", "adoEOF")

  其中icrsint.h檔案包含了VC++擴充套件的一些預處理指令、宏等的定義,用於COM程式設計時使用。

  2、定義_ConnectionPtr型變數,並建立資料庫連線

  建立了與資料庫伺服器的連線後,才能進行其他有關資料庫的訪問和操作。ADO使用Connection物件來建立與資料庫伺服器的連線,所以它相當於MFC中的CDatabase類。和CDatabase類一樣,呼叫Connection物件的Open方法即可建立與伺服器的連線。

  資料型別 _ConnectionPtr實際上就是由類别範本_com_ptr_t而得到的一個具體的例項類,其定義可以到msado15.tlh、comdef.h 和comip.h這三個檔案中找到。在msado15.tlh中有:

_COM_SMARTPTR_TYPEDEF(_Collection, __uuidof(_Collection));

  經宏擴充套件後就得到了_ConnectionPtr類。_ConnectionPtr類封裝了Connection物件的Idispatch介面指標,及一些必要的操作。我們就是透過這個指標來操縱Connection物件。類似地,後面用到的_CommandPtr和_RecordsetPtr型別也是這樣得到的,它們分別表示命令物件指標和記錄集物件的指標。

  (1)、連線到MS SQL Server

  注意連線字串的格式,提供正確的連線字串是成功連線到資料庫伺服器的第一步,有關連線字串的詳細資訊參見微軟MSDN Library光碟。

  本例連線字串中的server_name,database_name,user_name和password在程式設計時都應該替換成實際的內容。

_ConnectionPtr pMyConnect=NULL;
HRESULT hr=pMyConnect.CreateInstance(__uuidof(Connection)));
if(FAILED(hr))return;

_bstr_t strConnect="Provider=SQLOLEDB; Server=server_name;"
"Database=database_name; uid=user_name; pwd=password;";
//connecting to the database server now:
try{pMyConnect->Open(strConnect,"","",NULL);}
catch (_com_error &e)
{
::MessageBox(NULL,e.Description(),"警告",MB_OK │ MB_ICONWARNING);
}

  注意Connection物件的Open方法中的連線字串引數必須是BSTR或_bstr_t型別。另外,本例是直接透過OLE DB Provider建立連線,所以無需建立資料來源。

  (2)、透過ODBC Driver連線到Database Server連線字串格式與直接用ODBC程式設計時的差不多:

_bstr_t strConnect="DSN=datasource_name; Database=database_name; uid=user_name; pwd=password;";

  此時與ODBC程式設計一樣,必須先建立資料來源。

  3、定義_RecordsetPtr型變數,並開啟資料集

  定義_RecordsetPtr型變數,然後透過它呼叫Recordset物件的Open方法,即可開啟一個資料集。所以Recordset物件與MFC中的CRecordset類類似,它也有當前記錄、當前記錄指標的概念。如:

_RecordsetPtr m_pRecordset;
if(!FAILED(m_pRecordset.CreateInstance( __uuidof( Recordset )))
{
m_pDoc->m_initialized=FALSE;
return;
}

try{
m_pRecordset->Open(_variant_t("mytable"),
_variant_t((IDispatch *)pMyConnect,true), adOpenKeyset,
adLockOptimistic, adCmdTable);
}
catch (_com_error &e)
{
::MessageBox(NULL,"無法開啟mytable表。","提示",
MB_OK │ MB_ICONWARNING);
}

  Recordset物件的Open方法非常重要,它的第一個引數可以是一個SQL語句、一個表的名字或一個命令物件等等;第二個引數就是前面建立的連線物件的指標。此外,用Connection和Command物件的Execute方法也能得到記錄集,但是隻讀的。 (待續1-2摘自ITDB資料庫)

4、讀取當前記錄的資料

  我認為讀取資料的最方便的方法如下:

try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
//Retrieve column's value:
CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t("name"))->Value);
short cAge=(short)(m_pRecordset->Fields->GetItem
(_variant_t("age"))->Value);
//Do something what you want to do:
......
m_pRecordset->MoveNext();
}
}//try
catch (_com_error &e)
{
CString str=(char*)e.Description();
::MessageBox(NULL,str+"n又出毛病了。","提示",
MB_OK │ MB_ICONWARNING);
}

  本例中的name和age都是欄位名,讀取的欄位值分別儲存在sName和cAge變數內。例中的Fields是Recordset物件的容器,GetItem方法返回的是Field物件,而Value則是Field物件的一個屬性(即該欄位的值)。透過此例,應掌握操縱物件屬性的方法。例如,要獲得Field 物件的Value屬性的值可以直接用屬性名Value來引用它(如上例),但也可以呼叫Get方法,例如:

CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t("name"))->GetValue());

  從此例還可以看到,判斷是否到達記錄集的末尾,使用記錄集的adoEOF屬性,其值若為真即到了結尾,反之則未到。判斷是否到達記錄集開頭,則可用BOF屬性。

  另外,讀取資料還有一個方法,就是定義一個繫結的類,然後透過繫結的變數得到欄位值(詳見後面的介紹)。

  5、修改資料

  方法一:

try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
m_pRecordset->Fields->GetItem
(_variant_t("姓名"))->Value=_bstr_t("趙薇");
......
m_pRecordset->Update();

m_pRecordset->MoveNext();
}
}//try

  改變了Value屬性的值,即改變了欄位的值。

  方法二:

m_pRecordset->Fields->GetItem
(_variant_t("姓名"))->PutValue(_bstr_t("趙薇"));

  方法三:就是用定義繫結類的方法(詳見後面的介紹)。

  6、新增記錄

  新記錄新增成功後,即自動成為當前記錄。AddNew方法有兩種形式,一個含有引數,而另一個則不帶引數。

  方法一(不帶引數):

// Add new record into this table:
try{
if(!m_pRecordset->Supports(adAddNew)) return;

m_pRecordset->AddNew();
m_pRecordset->Fields->GetItem
(_variant_t("姓名"))->Value=_bstr_t("趙薇");
m_pRecordset->Fields->GetItem
(_variant_t("性別"))->Value=_bstr_t("女");
m_pRecordset->Fields->GetItem
(_variant_t("age"))->Value=_variant_t((short)20);
m_pRecordset->Fields->GetItem
(_variant_t("marry"))->Value=_bstr_t("未婚");
m_pRecordset->Update();
}//try
catch (_com_error &e)
{
::MessageBox(NULL, "又出毛病了。","提示",MB_OK │ MB_ICONWARNING);
}

  這種方法弄完了還要呼叫Update()。

  方法二(帶引數):

_variant_t varName[4],narValue[4];
varName[0] = L"姓名";
varName[1] = L"性別";
varName[2] = L"age";
varName[3] = L"marry";
narValue[0]=_bstr_t("趙薇");
narValue[1]=_bstr_t("女");
narValue[2]=_variant_t((short)20);
narValue[3]=_bstr_t("未婚");

const int nCrit = sizeof varName / sizeof varName[0];
// Create SafeArray Bounds and initialize the array
SAFEARRAYBOUND rgsaName[1],rgsaValue[1];
rgsaName[0].lLbound = 0;
rgsaName[0].cElements = nCrit;
SAFEARRAY *psaName = SafeArrayCreate( VT_VARIANT, 1, rgsaName );
rgsaValue[0].lLbound = 0;
rgsaValue[0].cElements = nCrit;
SAFEARRAY *psaValue = SafeArrayCreate( VT_VARIANT, 1, rgsaValue );
// Set the values for each element of the array
HRESULT hr1=S_OK.hr2=S_OK;
for( long i = 0 ; i < nCrit && SUCCEEDED( hr1 ) && SUCCEEDED( hr2 );i++)
{
hr1=SafeArrayPutElement(psaName, &i,&varName[i]);
hr2=SafeArrayPutElement(psaValue, &i,&narValue[i]); }

// Initialize and fill the SafeArray
VARIANT vsaName,vsaValue;
vsaName.vt = VT_VARIANT │ VT_ARRAY;
vsaValue.vt = VT_VARIANT │ VT_ARRAY;
V_ARRAY(&vsaName) = psaName;//&vsaName->parray=psaName;
//see definition in oleauto.h file.
V_ARRAY(&vsaValue) = psaValue;

// Add a new record:
m_pRecordset->AddNew(vsaName,vsaValue);

  這種方法不需要呼叫Update,因為新增後,ADO會自動呼叫它。此方法主要是使用SafeArray挺麻煩。

  方法三:就是用定義繫結類的方法(詳見後面的介紹)。

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

相關文章