標籤:des blog io os ar 使用 for strong sp
讀寫資料庫的技術很多,現在多用ADO。ADO以COM方式提供,所以它的很多行為遵循COM規範。首先,要引入ADO的COM檔案,它的位置一般在"C:/Program Files/Common Files/System/ado/msado15.dll"。
1. 引入ADO
開啟先行編譯標頭檔StdAfx.h,寫上引入聲明:
#import "C:/Program Files/Common Files/System/ado/msado15.dll" no_namespace rename("EOF","adoEOF")
解釋一下上句:no_namespace是指忽略命名空間,rename則是把ADO中的EOF重新命名為adoEOF。命成什麼名字無所謂,但注意聲明中的名字要和代碼中的名字一致。
2. 初始化
用ADO寫代碼前,要將COM初始化。常用手段是在代碼前後加上CoInitialize(NULL)和CoUninitialize()。也可以用AfxOleInit()來初始化COM庫。
3. 三個核心對象
ADO的3個核心對象是連線物件(_Connection)、命令對象(_Command)和記錄集對象(_RecordSet)。其中連線物件是任何操作必須的。很多操作3個核心對象都可以完成。要執行個體化它們並使用它們提供的方法,不得不說到它們是一種智能指標(Smart Pointer)。在初始化或釋放等操作時,它們是一個對象,用點操作符,其他大部分操作則使用“->”操作符。
4. 執行個體化
_ConnectionPtr pConn(__uuidof(Connection));
_RecordsetPtr pRec(__uuidof(Recordset));
_CommandPtr pCmd(__uuidof(Command));
如果上面不加參數,則需加上:
pConn.CreateInstance("ADODB.Connection");
pRec.CreateInstance("ADODB.Recordset");
pCmd.CreateInstance("ADODB.Command");
5. 串連資料庫
串連資料庫一般採取字串串連。這個字串的擷取方法用了“不能說的秘密”,即任意建立一個txt檔案,重新命名為.x.udl。然後雙擊此檔案,將出現“資料庫連接屬性”視窗。第一個標籤頁“提供者”列出了所有資料庫引擎,Access, SQL Server, Oracle等,選擇後點下一步跳至第二個標籤頁“串連”,選擇伺服器名稱欄可以填上伺服器的IP地址,本機則可不填或填點號;填上資料庫使用者名稱和密碼後就可以選擇資料庫了。點“測試連接”按鈕,成功。確定。用記事本開啟x.udl。將會看到它產生的連接字串。如下:
"Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password=123;Initial Catalog=cfdata"
此串連串中,Persist Security Info屬性為True時表示在建立串連後仍然儲存密碼,一般取False即可。ID和Password屬性只有在上述資料庫屬性對話方塊中勾選“允許儲存密碼”時才會有。自己可以手工添加。Cfdata是我的資料庫名。
C++中串連代碼如下:
pConn->ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password=123;Initial Catalog=cfdata";
6. 樣本
有些資料庫操作_Connection一個就能完全搞定。如update語句。因為它不需要返回的結果。如下:
pConn->ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password=123;Initial Catalog=cfdata";
pConn->Open("","","",adConnectUnspecified); //開啟串連。此處參數均已在上述字串聲明,故可為空白
CString strSQL="update table1 set name=’Richard’ where id=1";
_pConn->Execute(_bstr_t(strSQL),NULL,adCmdText);
這裡涉及強制類型轉換。COM中的資料類型和常規(如MFC)類型一般都有對應,但需要轉換。如上面的_bstr_t和CString。至於應該轉換成什麼類型,看VC環境中的提示即可(這裡推薦大家加裝Visual Assitant,使提示功能更完善)。
也有些操作需要返回記錄集,如select語句。這裡就至少需要_Connection和_Recordset兩種核心對象,也可以用_Command執行之。下面展示同一操作用3種對象分別實現的代碼。
(1)連線物件
CString strSQL="select * from table1"; //方法1
pRec=pConn->Execute(_bstr_t(strSQL),NULL,adCmdText);
(2)記錄集對象
CString strSQL="select * from table1"; //方法2
pRec->Open(_variant_t(strSQL),(_variant_t)( (IDispatch*)pConn),adOpenDynamic,adLockOptimistic,adCmdText);
其中第二個參數(_variant_t)( (IDispatch*)pConn)指明活動串連,資料類型轉換比較複雜,_variant_t是參數要求的類型,IDispath*則是_variant_t可強制轉換類型類型。也可用下句:
pConnGetInterfacePtr();
(3) 命令對象
CString strSQL="select * from table1"; //方法3
pCmd->put_ActiveConnection((_variant_t)((IDispatch*)pConn));
pCmd->CommandText=_bstr_t(strSQL);
pRec=pCmd->Execute(NULL,NULL,adCmdText);
7. 資料使用
取得記錄集後,將其中資料取出。用一個ListBox讀取其中name欄位資料。代碼如下:
while(!pRec->adoEOF)
{
//_bstr_t類型可以視作COM類型字串和MFC類型字串之間的橋樑
CString str=LPSTR(_bstr_t(pRec->GetCollect("name")));
((CListBox*)GetDlgItem(IDC_LIST1))->AddString(str);
pRec->MoveNext();
}
上述代碼中用到了adoEOF,要注意直接拷貝第三方代碼時可能會被重新命名為rsEOF等,此時則需作相應更改。另外,while迴圈中的MoveNext()也不要忘了,否則它就成了死迴圈了。欄位值的擷取及轉換也可用下面方法:
_variant_t var=pRec->GetCollect("name");
var.ChangeType(VT_BSTR);
CString str=var.bstrVal;
8. 關閉與釋放
用完相應對象後,需要關閉並釋放之,代碼如下:
pRec->Close();
pConn->Close();
pRec.Release();
pCmd.Release();
pConn.Release();
9. 錯誤捕獲
資料庫操作難免出現錯誤,串連串錯誤,SQL語句錯誤,或返回NULL你卻硬要向表裡塞(此錯誤可以在取出後用var.vt!=VT_NULL判斷),所以我們需要把它們放到try…catch段中。ADO在捕獲到錯誤後會拋出_com_error類型異常,我們可以這樣做:
try
{
pConn->ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password=123;Initial Catalog=cfdata";
pConn->Open("","","",adConnectUnspecified);
………… //代碼省略
}
catch(_com_error& e)
{
AfxMessageBox(e.ErrorMessage());
AfxMessageBox(e.Description());
}
這裡有個疑惑,在捕獲錯誤後,e.ErrorMessage()和e.Description()中放著不同資訊,有時前者說得清晰,有的後者說的清晰,搞不清楚,索性就都加上吧。最後,可以再加個catch(…),畢竟ADO之外的地方也可能發生錯誤。
這裡,我還犯過一個錯誤,被它整了N久。我把_com_error& e寫成了_com_error* e後面也對應改成->操作符,而且編譯通過,結果一運行程式就崩潰,而且它不告訴我在哪出錯,因為這時的錯誤是_com_error這時卻對著_com_error*來捕獲當然捕不到。這裡&只是一個引用,寫不寫無所謂,*是萬萬不可地。(網上書上很多大師級代碼都是用了*,誤導啊)。
10. 其他介面
ADO中除了3個核心對象外,我們還應瞭解FieldsPtr、FieldPtr、StreamPtr等介面。例如上述樣本中,我們可以用FieldsPtr的GetCount()方法擷取其欄位個數,用FieldPtr來接收具體欄位等。位元據像檔案等則會用到StreamPtr。
執行個體代碼:
#include <iostream>#include "vector"#import "C:\Program Files\Common Files\System\ado\msado15.dll" no_namespace rename("EOF","adoEOF")using namespace std;int main(int argc, char* argv[]){CoInitialize(NULL);_ConnectionPtr pConn(__uuidof(Connection));_RecordsetPtr pRec(__uuidof(Recordset));////或者採用以下方式://_ConnectionPtr pConn=NULL;//_RecordsetPtr pRec=NULL;//_CommandPtr pCmd=NULL;//pConn->CreateInstance("ADODB.Connection");//pRec->CreateInstance("ADODB.Recordset");//pCmd->CreateInstance("ADODB.Command"); try{_bstr_t strConnect = "Provider=SQLOLEDB;Persist Security Info=True;User Id=sa;Password=123456;Initial Catalog=Test;Data Source=localhost";//_bstr_t strConnect = "Provider=SQLOLEDB;Server=localhost;DataBase=Test;uid=sa;pwd=123456";pConn->Open(strConnect,"","",adModeUnknown);}catch(_com_error &e){cout<<"Initial failed!"<<endl;cout<<e.Description()<<endl;cout<<e.HelpFile()<<endl;return 0;}try{pRec = pConn->Execute("select * from CorrespondField",NULL,adCmdText);if(!pRec->BOF){pRec->MoveFirst();}else{cout<<"Data is empty!"<<endl;return 0;}vector<_bstr_t> column_name;for(int i=0;i<pRec->Fields->GetCount();i++){cout<<pRec->Fields->GetItem(_variant_t((long)i))->Name<<" ";column_name.push_back(pRec->Fields->GetItem(_variant_t((long)i))->Name);cout<<endl;}while(!pRec->adoEOF){for(vector<_bstr_t>::iterator Itr = column_name.begin();Itr!=column_name.end();Itr++){if (pRec->GetCollect(*Itr).vt != VT_NULL){cout<<(_bstr_t)pRec->GetCollect(*Itr)<<" ";}else{cout<<"NULL"<<endl;}}pRec->MoveNext();cout<<endl;}}catch (_com_error &e){cout<<e.Description()<<endl;cout<<e.HelpFile()<<endl;return 0;}try{pRec->Close();pConn->Close();pRec->Release();pConn->Release();}catch(_com_error &e){cout<<e.Description()<<endl;cout<<e.HelpFile()<<endl;return 0;}CoUninitialize();return 0;}
C++使用ADO串連資料庫及其執行個體