ADO学习(一)基础理论
一、ADO概述
ADO即Microsoft?ActiveXData?Object,是Microsoft繼ODBC之后,基于OLE?DB技術(shù)的一種數(shù)據(jù)庫操作技術(shù),使您能夠編寫通過?OLE?DB提供者對在數(shù)據(jù)庫服務(wù)器中的數(shù)據(jù)進(jìn)行訪問和操作的應(yīng)用程序。ADO同OLE?DB、數(shù)據(jù)庫之間的關(guān)系可以用下圖來表示:
其主要優(yōu)點(diǎn)是易于使用、高速度、低內(nèi)存支出和占用磁盤空間較少。ADO支持用于建立基于客戶端/服務(wù)器和?Web的應(yīng)用程序的主要功能。
?
?
二、ADO初步認(rèn)識
????????在此介紹的ADO編程技術(shù)都是基于C++的,要熟練掌握如何用ADO操作數(shù)據(jù)庫,首先對ADO技術(shù)得有個(gè)總體的了解,AD0主要由幾個(gè)對象組成:Connection、Command、Paramenter、Recorderset、Fields、Error、Property對象,對數(shù)據(jù)庫的操作,都是通過這幾個(gè)對象來進(jìn)行的,對象模型的關(guān)系圖如下:
?
·??????Connection對象
在數(shù)據(jù)庫應(yīng)用里操作數(shù)據(jù)源都必須通過該對象,這是數(shù)據(jù)交換的環(huán)境。Connection對象代表了同數(shù)據(jù)源的一個(gè)會話,在客戶/服務(wù)器模型里,這個(gè)會話相當(dāng)于同服務(wù)器的一次網(wǎng)絡(luò)連接。不同的數(shù)據(jù)提供者提供的該對象的集合、方法和屬性不同。
借助于Connection對象的集合、方法和屬性,可以使用Open和Close方法建立和釋放一個(gè)數(shù)據(jù)源連接。使用Execute方法可以執(zhí)行一個(gè)數(shù)據(jù)操作命令,使用BeginTrans、CommitTrans和RollbackTrans方法可以啟動、提交和回滾一個(gè)處理事務(wù)。通過操作the?Errors?集合可以獲取和處理錯(cuò)誤信息,操作CommandTimeout屬性可以設(shè)置連接的溢出時(shí)間,操作ConnectionString屬性可以設(shè)置連接的字符串,操作Mode屬性可以設(shè)置連接的模式,操作Provider屬性可以指定OLE?DB提供者。
·??????Command對象
Command對象是一個(gè)對數(shù)據(jù)源執(zhí)行命令的定義,使用該對象可以查詢數(shù)據(jù)庫并返回一個(gè)Recordset對象,可以執(zhí)行一個(gè)批量的數(shù)據(jù)操作,可以操作數(shù)據(jù)庫的結(jié)構(gòu)。不同的數(shù)據(jù)提供者提供的該對象的集合、方法和屬性不同。
借助于Command對象的集合、方法和屬性,可以使用Parameters集合制定命令的參數(shù),可以使用Execute方法執(zhí)行一個(gè)查詢并將查詢結(jié)果返回到一個(gè)Recordset對象里,操作CommandText屬性可以為該對象指定一個(gè)命令的文本,操作CommandType屬性可以指定命令的類型,操作Prepared可以得知數(shù)據(jù)提供者是否準(zhǔn)備好命令的執(zhí)行,操作CommandTimeout屬性可以設(shè)置命令執(zhí)行的溢出時(shí)間。
·??????Parameter對象
Parameter對象在Command對象中用于指定參數(shù)化查詢或者存儲過程的參數(shù)。大多數(shù)數(shù)據(jù)提供者支持參數(shù)化命令,這些命令往往是已經(jīng)定義好了的,只是在執(zhí)行過程中調(diào)整參數(shù)的內(nèi)容。
借助于Parameter對象的集合、方法和屬性,可以通過設(shè)置Name屬性指定參數(shù)的名稱,通過設(shè)置Value屬性可以指定參數(shù)的值,通過設(shè)置Attributes和Direction、Precision、NumericScale、Size與Type屬性可以指定參數(shù)的信息,通過執(zhí)行AppendChunk方法可以將數(shù)據(jù)傳遞到參數(shù)里。
·??????Recordset對象
如果執(zhí)行的命令是一個(gè)查詢并返回存放在表中的結(jié)果集,這些結(jié)果集將被保存在本地的存儲區(qū)里,Recordset對象是執(zhí)行這種存儲的ADO對象。通過Recordset對象可以操縱來自數(shù)據(jù)提供者的數(shù)據(jù),包括修改和更新行、插入和刪除行。
ADO定義了如表
| 光標(biāo)類型 | 描述 |
| adOpenDynamic | 允許添加、修改和刪除記錄,支持所有方式的光標(biāo)移動,其他用戶的修改可以在聯(lián)機(jī)以后仍然可見 |
| adOpenKeyset | 類似于adOpenDynamic光標(biāo),它支持所有類型的光標(biāo)移動,但是建立連接以后其他用戶對記錄的添加不可見,其他用戶對記錄的刪除和對數(shù)據(jù)的修改是可見的。支持書簽操作 |
| adOpenStatic | 支持各種方式的光標(biāo)移動,但是建立連接以后其他用戶的行添加、行刪除和數(shù)據(jù)修改都不可見,支持書簽操作 |
| adOpenForwardOnly | 只允許向前存取,而且在建立連接以后,其他用戶的行添加、行刪除和數(shù)據(jù)修改都不可見,支持書簽操作 |
ADO定義了如表
| 鎖定類型 | 描述 |
| adLockReadOnly | (缺省)數(shù)據(jù)只讀 |
| adLockPessimistic | 鎖定操作的所有行,也稱為消極鎖定 |
| adLockOptimistic | 只在調(diào)用Update方法時(shí)鎖定操作的行,也稱為積極鎖定 |
| adLockBatchOptimistic | 在批量更新時(shí)使用該鎖定,也稱為積極批量鎖定 |
ADO定義了如表
| 光標(biāo)服務(wù)位置 | 描述 |
| adUseNone | 不使用光標(biāo)服務(wù)位置 |
| adUseClient | 使用客戶端光標(biāo) |
| adUseServer | (缺省)使用數(shù)據(jù)服務(wù)端或者驅(qū)動提供端光標(biāo) |
借助于Recordset對象的集合、方法和屬性,可以通過設(shè)置CursorType屬性設(shè)置記錄集的光標(biāo)類型,通過設(shè)置CursorLocation屬性可以指定光標(biāo)位置,通過讀取BOF和EOF屬性的值,獲知當(dāng)前光標(biāo)在記錄集里的位置是在最前或者最后,通過執(zhí)行MoveFirst、MoveLast、MoveNext和MovePrevious方法移動記錄集里的光標(biāo),通過執(zhí)行Update方法可以更新數(shù)據(jù)修改,通過執(zhí)行AddNew方法可以執(zhí)行行插入操作,通過執(zhí)行Delete方法可以刪除行。
·??????Field對象
Recordset對象的一個(gè)行由一個(gè)或者多個(gè)Fields對象組成,如果把一個(gè)Recordset對象看成一個(gè)二維網(wǎng)格表,那么Fields對象就是這些列。這些列里保存了列的名稱、數(shù)據(jù)類型和值,這些值是來自數(shù)據(jù)源的真正數(shù)據(jù)。為了修改數(shù)據(jù)源里的數(shù)據(jù),必須首先修改Recordset對象各個(gè)行里Field對象里的值,最后Recordset對象將這些修改提交到數(shù)據(jù)源。
借助于Field對象的集合、方法和屬性,可以通過讀取Name屬性,獲知列的名稱。通過操作Value屬性可以改變列的值,通過讀取Type、Precision和NumericScale屬性,可獲知列的數(shù)據(jù)類型、精度和小數(shù)位的個(gè)數(shù),通過執(zhí)行AppendChunk和GetChunk方法可以操作列的值。
·??????Error對象
Error對象包含了ADO數(shù)據(jù)操作時(shí)發(fā)生錯(cuò)誤的詳細(xì)描述,ADO的任何對象都可以產(chǎn)生一個(gè)或者多個(gè)數(shù)據(jù)提供者錯(cuò)誤,當(dāng)錯(cuò)誤發(fā)生時(shí),這些錯(cuò)誤對象被添加到Connection對象的Errors集合里。當(dāng)另外一個(gè)ADO對象產(chǎn)生一個(gè)錯(cuò)誤時(shí),Errors集合里的Error對象被清除,新的Error對象將被添加到Errors集合里。
借助于Errosr對象的集合、方法和屬性,可以通過讀取Number和Description屬性,獲得ADO錯(cuò)誤號碼和對錯(cuò)誤的描述,通過讀取Source屬性得知錯(cuò)誤發(fā)生的源。
·??????Property對象
Property對象代表了一個(gè)由提供者定義的ADO對象的動態(tài)特征。ADO對象有兩種類型的Property對象:內(nèi)置的和動態(tài)的。內(nèi)置的Property對象是指那些在ADO里實(shí)現(xiàn)的在對象創(chuàng)建時(shí)立即可見的屬性,可以通過域作用符直接操作這些屬性。動態(tài)的Property對象是指由數(shù)據(jù)提供者定義的底層的屬性,這些屬性出現(xiàn)在ADO對象的Properties集合里,例如,如果一個(gè)Recordset對象支持事務(wù)和更新,這些屬性將作為Property對象出現(xiàn)在Recordset對象的Properties集合里。動態(tài)屬性必須通過集合進(jìn)行引用,比如使用下面的語法:
???????MyObject.Properties(0)???
或者
???????MyObject.Properties("Name")
不能刪除任何類型的屬性對象。借助于Property對象的集合、方法和屬性,可以通過讀取Name屬性獲得屬性的名稱,通過讀取Type屬性獲取屬性的數(shù)據(jù)類型,通過讀取Value屬性獲取屬性的值。
三、ADO編程
?
ADO是以DLL封裝的,要使用ADO,首先得引入其DLL庫,引入ADO類型庫的方法有多種,在此我們就介紹一種----通過預(yù)處理指令#import引入。一般情況下,在windows操作系統(tǒng)的Program?Files\Common?Files\System\ado目錄下都有一個(gè)msado*.dll文件,根據(jù)windows版本不同,該文件可以是msado1.dll、msado15.dll、msado2.dll,對于此文件,版本不一樣,可能導(dǎo)致一些沖突,如:系統(tǒng)自帶的msado*.dll可能與程序編譯所用的版本不一樣,可能會導(dǎo)致一些異常,那么對于這種情況,我個(gè)人一般的做法是,在軟件目錄下面帶上msado*.dll文件,這樣就排除了對軟件運(yùn)行環(huán)境的依賴。
?
Ado類型庫引入后,程序在編譯過程中,VC++會讀出msado*.dll中的類型庫信息,自動產(chǎn)生兩個(gè)該類型庫的頭文件和實(shí)現(xiàn)文件msado15.tlh和msado15.tli(在您的Debug或Release目錄下)。在這兩個(gè)文件里定義了ADO的所有對象和方法,以及一些枚舉型的常量等。以下是引入方法:
#import"..\CommFile\Lib\msado15.dll"?named_guids?rename("EOF","adoEOF"),rename("BOF","adoBOF")
//重命名EOF與BOF以免與其他命名空間沖突
但要注意不能放在stdAfx.h文件的開頭,而應(yīng)該放在所有include指令的后面。否則在編譯時(shí)會出錯(cuò)。
初始化COM
????????ADO是COM組件,使用任何COM組件的時(shí)候,都要先初始化COM,其實(shí)就是在使用COM組件前調(diào)用一個(gè)API----CoInitialize(),在使用ADO對象之前調(diào)用即可,以下為代碼:
HRESULT?hr?=CoInitialize(NULL);
ASSERT(SUCCEEDED(hr));//返回值可判斷初始化COM是否成功,請用SUCCEEDED來判斷
一般情況下,使用完COM之后,調(diào)用反初始化COM函數(shù)----CoUninitialize()。一般會在數(shù)據(jù)庫訪問封裝類的析構(gòu)函數(shù)中調(diào)用。
三個(gè)核心對象
ADO的3個(gè)核心對象是連接對象(_Connection)、命令對象(_Command)和記錄集對象(_RecordSet)。其中連接對象是任何操作必須的。很多操作3個(gè)核心對象都可以完成。要實(shí)例化它們并使用它們提供的方法,不得不說到它們是一種智能指針(Smart?Pointer)。在初始化或釋放等操作時(shí),它們是一個(gè)對象,用點(diǎn)操作符,其他大部分操作則使用“->”操作符。
?實(shí)例化
???????_ConnectionPtr?pConn(__uuidof(Connection));
???????_RecordsetPtr?pRec(__uuidof(Recordset));
???????_CommandPtr?pCmd(__uuidof(Command));
如果上面不加參數(shù),則需加上:
??????????????pConn.CreateInstance("ADODB.Connection");
??????????????pRec.CreateInstance("ADODB.Recordset");
??????????????pCmd.CreateInstance("ADODB.Command");
有些數(shù)據(jù)庫操作_Connection一個(gè)就能完全搞定。如update語句。因?yàn)樗恍枰祷氐慕Y(jié)果。也有些操作需要返回記錄集,如select語句。這里就至少需要_Connection和_Recordset兩種核心對象,也可以用_Command執(zhí)行之。
?
錯(cuò)誤捕獲
數(shù)據(jù)庫操作難免出現(xiàn)錯(cuò)誤,連接串錯(cuò)誤,SQL語句錯(cuò)誤,或返回NULL你卻硬要向表里塞(此錯(cuò)誤可以在取出后用var.vt!=VT_NULL判斷),所以我們需要把它們放到try…catch段中。ADO在捕獲到錯(cuò)誤后會拋出_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());
???????}
????????這里有個(gè)疑惑,在捕獲錯(cuò)誤后,e.ErrorMessage()和e.Description()中放著不同信息,有時(shí)前者說得清晰,有的后者說的清晰,搞不清楚,索性就都加上吧。最后,可以再加個(gè)catch(…),畢竟ADO之外的地方也可能發(fā)生錯(cuò)誤。
這里,我還犯過一個(gè)錯(cuò)誤,被它整了N久。我把_com_error&?e寫成了_com_error*?e后面也對應(yīng)改成->操作符,而且編譯通過,結(jié)果一運(yùn)行程序就崩潰,而且它不告訴我在哪出錯(cuò),因?yàn)檫@時(shí)的錯(cuò)誤是_com_error這時(shí)卻對著_com_error*來捕獲當(dāng)然捕不到。這里&只是一個(gè)引用,寫不寫無所謂,*是萬萬不可地。(網(wǎng)上書上很多大師級代碼都是用了*,誤導(dǎo)啊)。
?
VARIANT和BSTR,_variant_t和_bstr_t
COM必須設(shè)計(jì)成跨平臺,需要提供語言無關(guān)的數(shù)據(jù)類型,多數(shù)編程語言有自己的字符串表示,需要定義一種通用的字符串類型,可以很容易的匹配到不同編程語言。
C++中,由于COM編程不使用CString,因?yàn)樗枰环N更普遍的方法來處理字符串和其他數(shù)據(jù)類型,這就是VARIANT變量數(shù)據(jù)和BSTR類型的來歷。
Variant?是一種特殊的數(shù)據(jù)類型,除了定長?String?數(shù)據(jù)及用戶定義類型外,可以包含任何種類的數(shù)據(jù)。_variant_t是一個(gè)類,它封裝了VARIANT類型。用ADO讀取數(shù)據(jù)庫中數(shù)據(jù)的
時(shí)候一般用_variant_t。
VARIANT的定義和用法可以參見tagVARIANT結(jié)構(gòu)體
----------------------------------------------------------------------------
typedef?struct?tagVARIANT?{
VARTYPE?vt;
unsigned?short?wReserved1;
unsigned?short?wReserved2;
unsigned?short?wReserved3;
union?{
unsigned?char?bVal;?//?VT_UI1.
short?iVal;?//?VT_I2?.
long?lVal;?//?VT_I4?.
……………………………………………………………………………………
void?FAR*?byref;?//?Generic?ByRef.
};
};
-----------------------------------------------------------------------------
下面是使用VARIANT的例子:
VARIANT?theValue;
//Initializes?a?variant——::VariantInit(&theValue);
int?value?=2007;
theValue.vt?=?VT_I4;?//?聲明是long類型(long?lVal;?//?VT_I4)
theValue.lVal?=?value;?//?賦值
如果theValue.vt=VT_ERROR;//沒有初始化
如果theValue.vt=VT_NULL;?//初始化了,但沒有值
如果theValue.vt=VT_EMPTY;?//賦值為空
BSTR(Basic?STRing,Basic字符串),用來處理COM中的字符串。BSTR是一個(gè)字符串指針,可以存放字符串。它的定義如下:
typedef?OLECHAR?*BSTR;
typedef?WCHAR?OLECHAR;
_bstr_t也是一個(gè)類,它封裝了BSTR類型。在操作數(shù)據(jù)庫時(shí),SQL語言可以用_bstr_t來存放。例如:
CString?sql="select?*?from?table";
m_pCnn->Open(_bstr_t(sql),"","",-1);
或者:
_bstr_t?sql="select?*?from?table";
m_pCnn->Open(sql,"","",-1);
或者:
CString?sql="select?*?from?table";
BSTR?bstrSQL=sql.AllocSysString();
m_pCnn->Open(bstrSQL,"","",-1);
除了_variant_t,_bstr_t之外,_com_error也是ado操作中常用到的COM支持類,在comdef.h中定義,使用這些類,可以避免在組件中引起的沖突。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的ADO学习(一)基础理论的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ADO学习(二).udl文件
- 下一篇: 破解SQLSERVER存储过程的加密