ADO数据库访问技术
https://blog.csdn.net/lishan9133/article/details/7055595
一、ADO(active data object,活動數(shù)據(jù)對象)實(shí)際上是一種基于COM(組件對象模型)的自動化接口技術(shù),并以O(shè)LE DB(對象連接和鑲?cè)氲臄?shù)據(jù)庫)為基礎(chǔ),經(jīng)過OLE DB精心包裝后的數(shù)據(jù)庫訪問技術(shù),利用它可以快速的創(chuàng)建數(shù)據(jù)庫應(yīng)用程序。ADO提供了一組非常簡單,將一般通用的數(shù)據(jù)訪問細(xì)節(jié)進(jìn)行封裝的對象。由于ODBC數(shù)據(jù)源也提供了一般的OLE DB Privider,所以ADO不僅可以應(yīng)用自身的OLE DB Privider,而且還可以應(yīng)用所有的ODBC驅(qū)動程序。
??? 萬事開頭難,任何一種新技術(shù)對于初學(xué)者來說最重要的還是"入門",掌握其要點(diǎn)。讓我們來看看ADO數(shù)據(jù)庫開發(fā)的基本流程吧!它的基本步驟如下:
(1)初始化COM庫,引入ADO庫定義文件
(2)用Connection對象連接數(shù)據(jù)庫
(3)利用建立好的連接,通過Connection、Command對象執(zhí)行SQL命令,或利用Recordset對象取得結(jié)果記錄集進(jìn)行查詢、處理。
(4)使用完畢后關(guān)閉連接釋放對象。
二、ADO的三個核心對象
???Connection對象:它表示到數(shù)據(jù)庫的連接,管理應(yīng)用程序和數(shù)據(jù)庫之間的通信。Command和Recordset對象都有一個ActiveConnection屬性,該屬性用來引用Connection對象。
???Command對象:被用來處理重復(fù)執(zhí)行的查詢,或處理需要檢查在存儲過程調(diào)用中的輸出或返回參數(shù)的值的查詢。
???Recordset對象:被用來獲取數(shù)據(jù)。Recordset對象存放查詢的結(jié)果,這些結(jié)果由數(shù)據(jù)的行(成為記錄)和列(稱為字段)組成。每一列都存放在Recordset的Fields集合中的一個Fields對象中。
??????
三、我們?nèi)圆捎迷瓗旖Y(jié)構(gòu),數(shù)據(jù)庫名Demo.mdb,庫內(nèi)表名DemoTable,表內(nèi)字段名為Name(姓名)和Age(年齡)的兩個字段,來構(gòu)造示例程序操作所需的Access數(shù)據(jù)庫。
????????首先,要用#import語句來引用支持ADO的組件類型庫(*.tlb),其中類型庫可以作為可執(zhí)行程序(DLL、EXE等)的一部分被定位在其自身程序中的附屬資源里,如:被定位在msado15.dll的附屬資源中,只需要直接用#import引用它既可。可以直接在Stdafx.h文件中加入下面語句來實(shí)現(xiàn):
#import "c:\program files\common files\system\ado\msado15.dll" rename ("EOF", "adoEOF")??(在一行上啊)??????
using namespace ADODB;?
其中路徑名可以根據(jù)自己系統(tǒng)安裝的ADO支持文件的路徑來自行設(shè)定。當(dāng)編譯器遇到#import語句時,它會為引用組件類型庫中的接口生成包裝類,#import語句實(shí)際上相當(dāng)于執(zhí)行了API涵數(shù)LoadTypeLib()。#import語句會在工程可執(zhí)行程序輸出目錄中產(chǎn)生兩個文件,分別為*.tlh(類型庫頭文件)及*.tli(類型庫實(shí)現(xiàn)文件),它們分別為每一個接口產(chǎn)生智能指針,并為各種接口方法、枚舉類型,CLSID等進(jìn)行聲明,創(chuàng)建一系列包裝方法。rename ("EOF", "adoEOF")說明將ADO中結(jié)束標(biāo)志EOF改為adoEOF,以避免和其它庫中命名相沖突。
??? 其次,在程序初始過程中需要初始化組件,一般可以用CoInitialize(NULL);來實(shí)現(xiàn),這種方法在結(jié)束時要關(guān)閉初始化的COM,可以用下面語句CoUnInitialize();來實(shí)現(xiàn)關(guān)閉初始化的COM。在MFC中還可以采用另一種方法來實(shí)現(xiàn)初始化COM,這種方法只需要一條語句便可以自動為我們實(shí)現(xiàn)初始化COM和結(jié)束時關(guān)閉COM的操作,語句如下所示: AfxOleInit();
??? 接著,就可以直接使用ADO的操作了。我們經(jīng)常使用的只是前面用#import語句引用類型庫時,生成的包裝類.tlh中聲明的智能指針中的三個,它們分別是_ConnectionPtr、_RecordsetPtr和_CommandPtr。下面分別對它們的使用方法進(jìn)行介紹:
1、_ConnectionPtr智能指針,通常用于打開、關(guān)閉一個庫連接或用它的Execute方法來執(zhí)行SQL命令語句(用法和_CommandPtr中的Execute方法類似)。
——打開一個庫連接。先創(chuàng)建一個實(shí)例指針,再用Open打開一個庫連接,它將返回一個IUnknown的自動化接口指針。代碼如下所示:
在這段代碼中我們是通過Connection對象的Open方法來進(jìn)行連接數(shù)據(jù)庫的,下面是該方法的原型:
HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options );
上述函數(shù)中參數(shù)ConnectionString為連接字串;參數(shù)UserID是用戶名;參數(shù)Password是登陸密碼;參數(shù)Options是連接選項(xiàng),用于指定Connection對象對數(shù)據(jù)的更新許可權(quán),一般情況下Options可以是如下幾個常量:
adModeUnknown:缺省。當(dāng)前的許可權(quán)未設(shè)置
adModeRead:只讀
adModeWrite:只寫
adModeReadWrite:可以讀寫
adModeShareDenyRead:阻止其它Connection對象以讀權(quán)限打開連接
adModeShareDenyWrite:阻止其它Connection對象以寫權(quán)限打開連接
adModeShareExclusive:阻止其它Connection對象以讀寫權(quán)限打開連接
adModeShareDenyNone:阻止其它Connection對象以任何權(quán)限打開連接
Connection對象除Open()方法外還有許多方法,我們先介紹Connection對象中兩個有用的屬性ConnectionTimeOut與State。ConnectionTimeOut用來設(shè)置連接的超時時間,需要在Open之前調(diào)用,例如:
m_pConnection->ConnectionTimeout = 5;///設(shè)置超時時間為5秒 m_pConnection->Open("Data Source=adotest;","","",adModeUnknown);
State屬性指明當(dāng)前Connection對象的狀態(tài),0表示關(guān)閉,1表示已經(jīng)打開,我們可以通過讀取這個屬性來作相應(yīng)的處理,例如
?
——關(guān)閉一個庫連接。如果連接狀態(tài)有效,則用Close方法關(guān)閉它并賦于它空值。代碼如下所示:
if(m_pConnection->State)m_pConnection->Close(); m_pConnection= NULL;?
2、_RecordsetPtr智能指針,可以用來打開庫內(nèi)數(shù)據(jù)表,并可以對表內(nèi)的記錄、字段等進(jìn)行各種操作。
——打開數(shù)據(jù)表。打開庫內(nèi)表名為DemoTable的數(shù)據(jù)表,代碼如下:
Open()方法的原型如下:
HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options )
上述函數(shù)中參數(shù)Source是數(shù)據(jù)查詢字符串;參數(shù)ActiveConnection是已經(jīng)建立好的連接(我們需要用Connection對象指針來構(gòu)造一個_variant_t對象);參數(shù)CursorType光標(biāo)類型,它可以是以下值之一;請看這個枚舉結(jié)構(gòu):
enum CursorTypeEnum { adOpenUnspecified = -1,///不作特別指定 adOpenForwardOnly = 0,///前滾靜態(tài)光標(biāo)。這種光標(biāo)只能向前瀏覽記錄集,比如用MoveNext向前滾動,這種方式可以提高瀏覽速度。但諸如BookMark, RecordCount,AbsolutePosition,AbsolutePage都不能使用 adOpenKeyset = 1,///采用這種光標(biāo)的記錄集看不到其它用戶的新增、刪除操作,但對于更新原有記錄的操作對你是可見的。 adOpenDynamic = 2,///動態(tài)光標(biāo)。所有數(shù)據(jù)庫的操作都會立即在各用戶記錄集上反應(yīng)出來。 adOpenStatic = 3///靜態(tài)光標(biāo)。它為你的記錄集產(chǎn)生一個靜態(tài)備份,但其它用戶的新增、刪除、更新操作對你的記錄集來說是不可見的。 };
參數(shù)LockType表示數(shù)據(jù)庫的鎖定類型,它可以是以下值之一,請看如下枚舉結(jié)構(gòu):
enum LockTypeEnum { adLockUnspecified = -1,///未指定 adLockReadOnly = 1,///只讀記錄集 adLockPessimistic = 2,悲觀鎖定方式。數(shù)據(jù)在更新時鎖定其它所有動作,這是最安全的鎖定機(jī)制 adLockOptimistic = 3,樂觀鎖定方式。只有在你調(diào)用Update方法時才鎖定記錄。在此之前仍然可以做數(shù)據(jù)的更新、插入、刪除等動作 adLockBatchOptimistic = 4,樂觀分批更新。編輯時記錄不會鎖定,更改、插入及刪除是在批處理模式下完成。 };
參數(shù)Options的含義請參考本文中對Connection對象的Execute()方法的介紹。
——讀取表內(nèi)數(shù)據(jù)。將表內(nèi)數(shù)據(jù)全部讀出并顯示在列表框內(nèi),m_AccessList為列表框的成員變量名。如果沒有遇到表結(jié)束標(biāo)志adoEOF,則用GetCollect(字段名)或m_pRecordset->Fields->GetItem(字段名)->Value方法,來獲取當(dāng)前記錄指針?biāo)傅淖侄沃?#xff0c;然后再用MoveNext()方法移動到下一條記錄位置。代碼如下所示:
_variant_t var; CString strName,strAge;try{if(!m_pRecordset->BOF)m_pRecordset->MoveFirst();else{AfxMessageBox("表內(nèi)數(shù)據(jù)為空");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();}// 默認(rèn)列表指向第一項(xiàng),同時移動記錄指針并顯示m_AccessList.SetCurSel(0);}catch(_com_error *e){AfxMessageBox(e->ErrorMessage());}——插入記錄。可以先用AddNew()方法新增一個空記錄,再用PutCollect(字段名,值)輸入每個字段的值,最后再Update()更新到庫中數(shù)據(jù)既可。其中變量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()方法移動到當(dāng)前記錄的前一條記錄、MoveNext()方法移動到當(dāng)前記錄的下一條記錄。但我們有時經(jīng)常需要隨意移動記錄指針到任意記錄位置時,可以使用Move(記錄號)方法來實(shí)現(xiàn),注意: Move()方法是相對于當(dāng)前記錄來移動指針位置的,正值向后移動、負(fù)值向前移動,如:Move(3),當(dāng)前記錄是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()更新數(shù)據(jù)庫既可。可以用上面方法移動記錄指針,修改字段值代碼如下所示:
try{// 假設(shè)對第二條記錄進(jìn)行修改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()來更新數(shù)據(jù)庫既可。代碼如下所示:
try{// 假設(shè)刪除第二條記錄m_pRecordset->MoveFirst();m_pRecordset->Move(1); // 從0開始m_pRecordset->Delete(adAffectCurrent); // 參數(shù)adAffectCurrent為刪除當(dāng)前記錄m_pRecordset->Update();}catch(_com_error *e){AfxMessageBox(e->ErrorMessage());}——關(guān)閉記錄集。直接用Close方法關(guān)閉記錄集并賦于其空值。代碼如下所示:
m_pRecordset->Close();m_pRecordset = NULL;3、CommandPtr智能指針,可以使用_ConnectionPtr或_RecordsetPtr來執(zhí)行任務(wù),定義輸出參數(shù),執(zhí)行存儲過程或SQL語句。
(補(bǔ)充::ADO Command 對象用于執(zhí)行面向數(shù)據(jù)庫的一次簡單查詢。此查詢可執(zhí)行諸如創(chuàng)建、添加、取回、刪除或更新記錄等動作。
如果該查詢用于取回?cái)?shù)據(jù),此數(shù)據(jù)將以一個 RecordSet 對象返回。這意味著被取回的數(shù)據(jù)能夠被 RecordSet 對象的屬性、集合、方法或事件進(jìn)行操作。
Command 對象的主要特性是有能力使用存儲查詢和帶有參數(shù)的存儲過程。)
——執(zhí)行SQL語句。先創(chuàng)建一個_CommandPtr實(shí)例指針,再將庫連接和SQL語句做為參數(shù),執(zhí)行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); // 執(zhí)行SQL語句,返回記錄集——執(zhí)行存儲過程。執(zhí)行存儲過程的操作和上面執(zhí)行SQL語句類似,不同點(diǎn)僅是CommandText參數(shù)中不再是SQL語句,而是存儲過程的名字,如Demo。另一個不同點(diǎn)就是在Execute()中參數(shù)由adCmdText(執(zhí)行SQL語句),改為adCmdStoredProc來執(zhí)行存儲過程。如果存儲過程中存在輸入、輸出參數(shù)的話,需要使用到另一個智能指針_ParameterPtr來逐次設(shè)置要輸入、輸出的參數(shù)信息,并將其賦于_CommandPtr中Parameters參數(shù)來傳遞信息,有興趣的讀者可以自行查找相關(guān)書籍或MSDN。執(zhí)行存儲過程的代碼如下所示:
_CommandPtr m_pCommand; m_pCommand.CreateInstance(__uuidof(Command));m_pCommand->ActiveConnection = m_pConnection; // 將庫連接賦于它 m_pCommand->CommandText = "Demo"; m_pCommand->Execute(NULL,NULL, adCmdStoredProc);?
四、SQL命令的執(zhí)行可以采用多種形式,下面我們一一進(jìn)行闡述。
1、我們還可以利用Connection對象的Execute方法執(zhí)行SQL命令
Execute()方法的原型如下所示:
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options )
其中CommandText是命令字串,通常是SQL命令。參數(shù)RecordsAffected是操作完成后所影響的行數(shù), 參數(shù)Options表示CommandText中內(nèi)容的類型,Options可以取如下值之一:adCmdText表明CommandText是文本命令;adCmdTable表明CommandText是一個表名;adCmdProc表明CommandText是一個存儲過程; adCmdUnknown表明CommandText內(nèi)容未知。Execute()函數(shù)執(zhí)行完后返回一個指向記錄集的指針 下面我們給出具體代碼并作說明:_variant_t RecordsAffected; ///執(zhí)行SQL命令:CREATE TABLE創(chuàng)建表格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); ///執(zhí)行SQL統(tǒng)計(jì)命令得到包含記錄條數(shù)的記錄集 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();///關(guān)閉記錄集 CString message; message.Format("共有%d條記錄",vCount.lVal); AfxMessageBox(message);///顯示當(dāng)前記錄條數(shù)
?2、利用Command對象來執(zhí)行SQL命令
上面已經(jīng)敘述過
3、直接用Recordset對象進(jìn)行查詢?nèi)〉糜涗浖?/p>
上面也敘述過了
總結(jié)
以上是生活随笔為你收集整理的ADO数据库访问技术的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTTP代理与DNS
- 下一篇: QT下以ADO连接ORACLE数据库