C++使用ADO連接數據庫及其實例


讀寫數據庫的技術很多,現在多用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;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM