ADO數據庫訪問技術

一、ADO(active data object,活動數據對象)實際上是一種基於COM(組件對象模型)的自動化接口技術,並以OLE DB(對象連接和鑲入的數據庫)爲基礎,經過OLE DB精心包裝後的數據庫訪問技術,利用它可以快速的創建數據庫應用程序。ADO提供了一組非常簡單,將一般通用的數據訪問細節進行封裝的對象。由於ODBC數據源也提供了一般的OLE DB Privider,所以ADO不僅可以應用自身的OLE DB Privider,而且還可以應用所有的ODBC驅動程序。

    萬事開頭難,任何一種新技術對於初學者來說最重要的還是"入門",掌握其要點。讓我們來看看ADO數據庫開發的基本流程吧!它的基本步驟如下:

(1)初始化COM庫,引入ADO庫定義文件

(2)用Connection對象連接數據庫

(3)利用建立好的連接,通過Connection、Command對象執行SQL命令,或利用Recordset對象取得結果記錄集進行查詢、處理。

(4)使用完畢後關閉連接釋放對象。

二、ADO的三個核心對象

   Connection對象:它表示到數據庫的連接,管理應用程序和數據庫之間的通信。Command和Recordset對象都有一個ActiveConnection屬性,該屬性用來引用Connection對象。

   Command對象:被用來處理重複執行的查詢,或處理需要檢查在存儲過程調用中的輸出或返回參數的值的查詢。

   Recordset對象:被用來獲取數據。Recordset對象存放查詢的結果,這些結果由數據的行(成爲記錄)和列(稱爲字段)組成。每一列都存放在Recordset的Fields集合中的一個Fields對象中。

      

三、我們仍採用原庫結構,數據庫名Demo.mdb,庫內表名DemoTable,表內字段名爲Name(姓名)和Age(年齡)的兩個字段,來構造示例程序操作所需的Access數據庫。

        首先,要用#import語句來引用支持ADO的組件類型庫(*.tlb),其中類型庫可以作爲可執行程序(DLL、EXE等)的一部分被定位在其自身程序中的附屬資源裏,如:被定位在msado15.dll的附屬資源中,只需要直接用#import引用它既可。可以直接在Stdafx.h文件中加入下面語句來實現:

#import "c:\program files\common files\system\ado\msado15.dll" rename ("EOF", "adoEOF")  (在一行上啊)      

using namespace ADODB; 

其中路徑名可以根據自己系統安裝的ADO支持文件的路徑來自行設定。當編譯器遇到#import語句時,它會爲引用組件類型庫中的接口生成包裝類,#import語句實際上相當於執行了API涵數LoadTypeLib()。#import語句會在工程可執行程序輸出目錄中產生兩個文件,分別爲*.tlh(類型庫頭文件)及*.tli(類型庫實現文件),它們分別爲每一個接口產生智能指針,併爲各種接口方法、枚舉類型,CLSID等進行聲明,創建一系列包裝方法。rename ("EOF", "adoEOF")說明將ADO中結束標誌EOF改爲adoEOF,以避免和其它庫中命名相沖突。
    其次,在程序初始過程中需要初始化組件,一般可以用CoInitialize(NULL);來實現,這種方法在結束時要關閉初始化的COM,可以用下面語句CoUnInitialize();來實現關閉初始化的COM。在MFC中還可以採用另一種方法來實現初始化COM,這種方法只需要一條語句便可以自動爲我們實現初始化COM和結束時關閉COM的操作,語句如下所示: AfxOleInit();
    接着,就可以直接使用ADO的操作了。我們經常使用的只是前面用#import語句引用類型庫時,生成的包裝類.tlh中聲明的智能指針中的三個,它們分別是_ConnectionPtr、_RecordsetPtr和_CommandPtr。下面分別對它們的使用方法進行介紹:

1、_ConnectionPtr智能指針,通常用於打開、關閉一個庫連接或用它的Execute方法來執行SQL命令語句(用法和_CommandPtr中的Execute方法類似)。
——打開一個庫連接。先創建一個實例指針,再用Open打開一個庫連接,它將返回一個IUnknown的自動化接口指針。代碼如下所示:

_ConnectionPtr	m_pConnection;
// 初始化COM,創建ADO連接等操作
AfxOleInit();
m_pConnection.CreateInstance(__uuidof(Connection));

// 在ADO操作中建議語句中要常用try...catch()來捕獲錯誤信息,
// 因爲它有時會經常出現一些意想不到的錯誤。jingzhou xu
try                 
{	
	// 打開本地Access庫Demo.mdb
	m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Demo.mdb","","",adModeUnknown);
        //通過JET數據庫引擎對ACCESS2000數據庫的連接:
}
catch(_com_error e)
{
	AfxMessageBox("數據庫連接失敗,確認數據庫Demo.mdb是否在當前路徑下!");
	return FALSE;
}      

在這段代碼中我們是通過Connection對象的Open方法來進行連接數據庫的,下面是該方法的原型:

HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options );

上述函數中參數ConnectionString爲連接字串;參數UserID是用戶名;參數Password是登陸密碼;參數Options是連接選項,用於指定Connection對象對數據的更新許可權,一般情況下Options可以是如下幾個常量:

adModeUnknown:缺省。當前的許可權未設置

adModeRead:只讀

adModeWrite:只寫

adModeReadWrite:可以讀寫

adModeShareDenyRead:阻止其它Connection對象以讀權限打開連接

adModeShareDenyWrite:阻止其它Connection對象以寫權限打開連接

adModeShareExclusive:阻止其它Connection對象以讀寫權限打開連接

adModeShareDenyNone:阻止其它Connection對象以任何權限打開連接

Connection對象除Open()方法外還有許多方法,我們先介紹Connection對象中兩個有用的屬性ConnectionTimeOut與State。ConnectionTimeOut用來設置連接的超時時間,需要在Open之前調用,例如:

m_pConnection->ConnectionTimeout = 5;///設置超時時間爲5秒 m_pConnection->Open("Data Source=adotest;","","",adModeUnknown);

State屬性指明當前Connection對象的狀態,0表示關閉,1表示已經打開,我們可以通過讀取這個屬性來作相應的處理,例如

 

——關閉一個庫連接。如果連接狀態有效,則用Close方法關閉它並賦於它空值。代碼如下所示:

if(m_pConnection->State)
        m_pConnection->Close();
m_pConnection= NULL;      

 

2、_RecordsetPtr智能指針,可以用來打開庫內數據表,並可以對錶內的記錄、字段等進行各種操作。
——打開數據表。打開庫內表名爲DemoTable的數據表,代碼如下:

_RecordsetPtr	m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset));

// 在ADO操作中建議語句中要常用try...catch()來捕獲錯誤信息,
// 因爲它有時會經常出現一些意想不到的錯誤。jingzhou xu
try
{
	m_pRecordset->Open("SELECT * FROM DemoTable",                // 查詢DemoTable表中所有字段
						theApp.m_pConnection.GetInterfacePtr(),	 // 獲取庫接庫的IDispatch指針
						adOpenDynamic,
						adLockOptimistic,
						adCmdText);
}
catch(_com_error *e)
{
	AfxMessageBox(e->ErrorMessage());
}      

Open()方法的原型如下:

HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options )

上述函數中參數Source是數據查詢字符串;參數ActiveConnection是已經建立好的連接(我們需要用Connection對象指針來構造一個_variant_t對象);參數CursorType光標類型,它可以是以下值之一;請看這個枚舉結構:

enum CursorTypeEnum { adOpenUnspecified = -1,///不作特別指定 adOpenForwardOnly = 0,///前滾靜態光標。這種光標只能向前瀏覽記錄集,比如用MoveNext向前滾動,這種方式可以提高瀏覽速度。但諸如BookMark, RecordCount,AbsolutePosition,AbsolutePage都不能使用 adOpenKeyset = 1,///採用這種光標的記錄集看不到其它用戶的新增、刪除操作,但對於更新原有記錄的操作對你是可見的。 adOpenDynamic = 2,///動態光標。所有數據庫的操作都會立即在各用戶記錄集上反應出來。 adOpenStatic = 3///靜態光標。它爲你的記錄集產生一個靜態備份,但其它用戶的新增、刪除、更新操作對你的記錄集來說是不可見的。 };

參數LockType表示數據庫的鎖定類型,它可以是以下值之一,請看如下枚舉結構:

enum LockTypeEnum { adLockUnspecified = -1,///未指定 adLockReadOnly = 1,///只讀記錄集 adLockPessimistic = 2,悲觀鎖定方式。數據在更新時鎖定其它所有動作,這是最安全的鎖定機制 adLockOptimistic = 3,樂觀鎖定方式。只有在你調用Update方法時才鎖定記錄。在此之前仍然可以做數據的更新、插入、刪除等動作 adLockBatchOptimistic = 4,樂觀分批更新。編輯時記錄不會鎖定,更改、插入及刪除是在批處理模式下完成。 };

參數Options的含義請參考本文中對Connection對象的Execute()方法的介紹。

——讀取表內數據。將表內數據全部讀出並顯示在列表框內,m_AccessList爲列表框的成員變量名。如果沒有遇到表結束標誌adoEOF,則用GetCollect(字段名)或m_pRecordset->Fields->GetItem(字段名)->Value方法,來獲取當前記錄指針所指的字段值,然後再用MoveNext()方法移動到下一條記錄位置。代碼如下所示:

_variant_t var;
CString strName,strAge;
	try
	{
		if(!m_pRecordset->BOF)
			m_pRecordset->MoveFirst();
		else
		{
			AfxMessageBox("表內數據爲空");
			return;
		}

		// 讀入庫中各字段並加入列表框中
		while(!m_pRecordset->adoEOF)
		{
			var = m_pRecordset->GetCollect("Name");
			if(var.vt != VT_NULL)
				strName = (LPCSTR)_bstr_t(var);
			var = m_pRecordset->GetCollect("Age");
			if(var.vt != VT_NULL)
				strAge = (LPCSTR)_bstr_t(var);

			m_AccessList.AddString( strName + " --> "+strAge );

			m_pRecordset->MoveNext();
		}

		// 默認列表指向第一項,同時移動記錄指針並顯示
		m_AccessList.SetCurSel(0);
	}
	catch(_com_error *e)
	{
		AfxMessageBox(e->ErrorMessage());
	}     

——插入記錄。可以先用AddNew()方法新增一個空記錄,再用PutCollect(字段名,值)輸入每個字段的值,最後再Update()更新到庫中數據既可。其中變量m_Name和m_Age分別爲姓名及年齡編輯框的成員變量名。代碼所下所示:

try
	{
		// 寫入各字段值
		m_pRecordset->AddNew();
		m_pRecordset->PutCollect("Name", _variant_t(m_Name));
		m_pRecordset->PutCollect("Age", atol(m_Age));
		m_pRecordset->Update();

		AfxMessageBox("插入成功!");
	}
	catch(_com_error *e)
	{
		AfxMessageBox(e->ErrorMessage());
	}      

——移動記錄指針。移動記錄指針可以通過MoveFirst()方法移動到第一條記錄、MoveLast()方法移動到最後一條記錄、MovePrevious()方法移動到當前記錄的前一條記錄、MoveNext()方法移動到當前記錄的下一條記錄。但我們有時經常需要隨意移動記錄指針到任意記錄位置時,可以使用Move(記錄號)方法來實現,注意: Move()方法是相對於當前記錄來移動指針位置的,正值向後移動、負值向前移動,如:Move(3),當前記錄是3時,它將從記錄3開始往後再移動3條記錄位置。代碼如下所示:

	try
	{
		int curSel = m_AccessList.GetCurSel();	
		// 先將指針移向第一條記錄,然後就可以相對第一條記錄來隨意移動記錄指針
		m_pRecordset->MoveFirst();
		m_pRecordset->Move(long(curSel));
		
	}
	catch(_com_error *e)
	{
		AfxMessageBox(e->ErrorMessage());
	}      

——修改記錄中字段值。可以將記錄指針移動到要修改記錄的位置處,直接用PutCollect(字段名,值)將新值寫入並Update()更新數據庫既可。可以用上面方法移動記錄指針,修改字段值代碼如下所示:

	try
	{
		// 假設對第二條記錄進行修改
		m_pRecordset->MoveFirst();
		m_pRecordset->Move(1);        // 從0開始
		m_pRecordset->PutCollect("Name", _variant_t(m_Name));
		m_pRecordset->PutCollect("Age", atol(m_Age));
		m_pRecordset->Update();
	}
	catch(_com_error *e)
	{
		AfxMessageBox(e->ErrorMessage());
	}      

——刪除記錄。刪除記錄和上面修改記錄的操作類似,先將記錄指針移動到要修改記錄的位置,直接用Delete()方法刪除它並用Update()來更新數據庫既可。代碼如下所示:

	try
	{
		// 假設刪除第二條記錄
		m_pRecordset->MoveFirst();
		m_pRecordset->Move(1);        // 從0開始
		m_pRecordset->Delete(adAffectCurrent);  // 參數adAffectCurrent爲刪除當前記錄
		m_pRecordset->Update();
	}
	catch(_com_error *e)
	{
		AfxMessageBox(e->ErrorMessage());
	}      

——關閉記錄集。直接用Close方法關閉記錄集並賦於其空值。代碼如下所示:

	m_pRecordset->Close();
	m_pRecordset = NULL;      

3、CommandPtr智能指針,可以使用_ConnectionPtr或_RecordsetPtr來執行任務,定義輸出參數,執行存儲過程或SQL語句。

(補充::ADO Command 對象用於執行面向數據庫的一次簡單查詢。此查詢可執行諸如創建、添加、取回、刪除或更新記錄等動作。

如果該查詢用於取回數據,此數據將以一個 RecordSet 對象返回。這意味着被取回的數據能夠被 RecordSet 對象的屬性、集合、方法或事件進行操作。

Command 對象的主要特性是有能力使用存儲查詢和帶有參數的存儲過程。)

——執行SQL語句。先創建一個_CommandPtr實例指針,再將庫連接和SQL語句做爲參數,執行Execute()方法既可。代碼如下所示:

_CommandPtr		m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection;  // 將庫連接賦於它
m_pCommand->CommandText = "SELECT * FROM DemoTable";  // SQL語句
m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText); // 執行SQL語句,返回記錄集      

——執行存儲過程。執行存儲過程的操作和上面執行SQL語句類似,不同點僅是CommandText參數中不再是SQL語句,而是存儲過程的名字,如Demo。另一個不同點就是在Execute()中參數由adCmdText(執行SQL語句),改爲adCmdStoredProc來執行存儲過程。如果存儲過程中存在輸入、輸出參數的話,需要使用到另一個智能指針_ParameterPtr來逐次設置要輸入、輸出的參數信息,並將其賦於_CommandPtr中Parameters參數來傳遞信息,有興趣的讀者可以自行查找相關書籍或MSDN。執行存儲過程的代碼如下所示:

_CommandPtr		m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
  m_pCommand->ActiveConnection = m_pConnection;  // 將庫連接賦於它
m_pCommand->CommandText = "Demo";  
  m_pCommand->Execute(NULL,NULL, adCmdStoredProc);      


四、SQL命令的執行可以採用多種形式,下面我們一一進行闡述。

1、我們還可以利用Connection對象的Execute方法執行SQL命令

Execute()方法的原型如下所示:

_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options )

其中CommandText是命令字串,通常是SQL命令。參數RecordsAffected是操作完成後所影響的行數, 參數Options表示CommandText中內容的類型,Options可以取如下值之一:adCmdText表明CommandText是文本命令;adCmdTable表明CommandText是一個表名;adCmdProc表明CommandText是一個存儲過程; adCmdUnknown表明CommandText內容未知。Execute()函數執行完後返回一個指向記錄集的指針
下面我們給出具體代碼並作說明:

_variant_t RecordsAffected; ///執行SQL命令:CREATE TABLE創建表格users,users包含四個字段:整形ID,字符串username,整形old,日期型birthday m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",&RecordsAffected,adCmdText); ///往表格裏面添加記錄 m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) VALUES (1, 'Washington',25,'1970/1/1')",&RecordsAffected,adCmdText); ///將所有記錄old字段的值加一 m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText); ///執行SQL統計命令得到包含記錄條數的記錄集 m_pRecordset = m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText); _variant_t vIndex = (long)0; _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一個字段的值放入vCount變量 m_pRecordset->Close();///關閉記錄集 CString message; message.Format("共有%d條記錄",vCount.lVal); AfxMessageBox(message);///顯示當前記錄條數

 2、利用Command對象來執行SQL命令

上面已經敘述過

3、直接用Recordset對象進行查詢取得記錄集

上面也敘述過了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章