摘 要 ODBC(Open DataBase Connectivity,開放數(shù)據(jù)庫連接)提供了一組應(yīng)用程序調(diào)用接口和一套運行支持環(huán)境,應(yīng)用程序可以使用標(biāo)準(zhǔn)的函數(shù)進行數(shù)據(jù)庫操作,而不必關(guān)心數(shù)據(jù)源來自于何種數(shù)據(jù)庫管理系統(tǒng)(DBMS),只要有相應(yīng)的驅(qū)動程序即可。本文介紹了ODBC的運行機制,著重討論了Visual C++ 1.5下應(yīng)用MFC進行ODBC編程的方法。
一、開放數(shù)據(jù)庫連接
ODBC(Open DataBase Connectivity,開放數(shù)據(jù)庫連接)是微軟開放服務(wù)結(jié)構(gòu)中有關(guān)數(shù)據(jù)庫的一個組成部分。它建立了一組規(guī)范,并提供了一組應(yīng)用程序調(diào)用接口。用這樣一組接口建立的應(yīng)用程序,對數(shù)據(jù)庫的操作不依賴于任何數(shù)據(jù)庫管理系統(tǒng),不直接與任何DBMS打交道,由此可實現(xiàn)應(yīng)用程序?qū)Σ煌珼BMS的共享。數(shù)據(jù)庫操作的“數(shù)據(jù)源”對應(yīng)用程序是透明的,所有的數(shù)據(jù)庫操作由對應(yīng)DBMS的ODBC驅(qū)動程序(ODBC Driver)完成。有了ODBC驅(qū)動程序,數(shù)據(jù)源就變得十分廣泛,它可以是本機的某種數(shù)據(jù)庫格式的文件(如本機DOS目錄下的Access文
件*.mdb),也可以是遠程數(shù)據(jù)庫文件(如Microsoft SQL Server);它可以是目前已知的某種DBMS格式,也可以是一種全新的數(shù)據(jù)庫格式?傊,它取決于提供了什么數(shù)據(jù)庫類型的驅(qū)動程序。
Visual C++中的ODBC主要是實現(xiàn)基于Windows的關(guān)系數(shù)據(jù)庫的應(yīng)用的共享。
二、ODBC管理器
在ODBC中,數(shù)據(jù)源是一個重要的概念,它是數(shù)據(jù)庫位置和數(shù)據(jù)庫類型等連接信息的總和。數(shù)據(jù)源在使用前必須通過ODBC管理器(Administrator)進行登錄。在登錄數(shù)據(jù)源時,要搞清數(shù)據(jù)源名(Datasource name)、數(shù)據(jù)庫文件名(Database name)和數(shù)據(jù)表格名(Table name)這三者的概念和相互關(guān)系:數(shù)據(jù)源實際是一種數(shù)據(jù)連接的抽象,數(shù)據(jù)源名是登錄時賦予的“連接”的名稱,以供應(yīng)用程序使用,至于該數(shù)據(jù)源下連接的是哪一個數(shù)據(jù)庫,則由數(shù)據(jù)庫文件名指出(如Access 2.0 for MS Offics中的.mdb文件);一個數(shù)據(jù)庫文件中可以包括若干個數(shù)據(jù)表格(table)和其他內(nèi)容。在關(guān)系@@09A05900.GIF;圖1 ODBC層次關(guān)系圖數(shù)據(jù)庫中,數(shù)據(jù)是以二維表格的方式存在于數(shù)據(jù)庫@@文件中,應(yīng)用程序最終的操作目標(biāo)即是這些表格中的行(row記錄)和列(columns字段)數(shù)據(jù)。對于foxprow數(shù)據(jù)源,數(shù)據(jù)庫文件名是“路徑名”,而該路徑下的所有數(shù)據(jù)文件(*.dbf)都屬于該“數(shù)據(jù)庫文件”名下的數(shù)據(jù)表格(table)。
ODBC管理器被裝在Control Panel里(ODBCINST.CPL)。通過該工具可以增添、修改或刪除數(shù)據(jù)源,也用來增添、刪除ODBC驅(qū)動程序,ODBC管理器把數(shù)據(jù)源和它們的連接信息保存在ODBC.INI、ODBCINST.INI和ODBCISAM.INI中。當(dāng)需要共享應(yīng)用程序時,只需按新的數(shù)據(jù)文件的類型和位置重新登錄即可。
三、ODBC應(yīng)用程序接口
ODBC API是一組標(biāo)準(zhǔn)的ODBC函數(shù)庫,除了一般的數(shù)據(jù)庫操作函數(shù)外,還包括一組函數(shù)(如SQLExec或SQLExecdirect)能夠內(nèi)嵌標(biāo)準(zhǔn)SQL查詢語句。SQL(Structured Query Language結(jié)構(gòu)化查詢語言)是一種存取關(guān)系型數(shù)據(jù)庫的標(biāo)準(zhǔn)語言,能夠定義、查詢、修改和控制數(shù)據(jù),簡單的語句能夠作用于整個數(shù)據(jù)表格,具有很強的功能。
同Windows 3.1 SDK中API類似,ODBC API也是基于句柄(handle)進行操作的。API函數(shù)按功能可分為以下幾類:
·數(shù)據(jù)源連接函數(shù),設(shè)置/獲取有關(guān)信息的函數(shù);
·準(zhǔn)備/提交執(zhí)行SQL查詢語句的函數(shù)和獲得數(shù)據(jù)的函數(shù);
·終止函數(shù)和異常處理函數(shù)。
上述函數(shù)的順序也表示了進行數(shù)據(jù)庫操作的一般順序。兩個問題需要特別說明,一是數(shù)據(jù)類型問題:數(shù)據(jù)源中的數(shù)據(jù)所具有的數(shù)據(jù)類型稱為SQL數(shù)據(jù)類型,這些數(shù)據(jù)類型在其數(shù)據(jù)源中可能比較特殊,不一定和ODBC SQL數(shù)據(jù)類型存儲方式一致,驅(qū)動程序把這些數(shù)據(jù)類型同ODBC SQL數(shù)據(jù)類型進行相互轉(zhuǎn)換,每一個ODBC SQL數(shù)據(jù)類型都相當(dāng)于一個ODBC C語言數(shù)據(jù)類型;二是函數(shù)的調(diào)用級別問題,并不是每一個ODBC驅(qū)動程序都支持所有的ODBC API函數(shù)調(diào)用,在應(yīng)用程序中,可以調(diào)用有關(guān)函數(shù)獲取驅(qū)動程序以支持層次方面的信息。
四、ODBC應(yīng)用編程
在Visual C++中,MFC (Microsoft Foundation class基本類庫)是經(jīng)過對Windows應(yīng)用程序中各個部件進行類的抽象而建立的一組預(yù)定義的類,如窗口基類(CWnd)、各種窗口派生類等等,這些類在應(yīng)用程序中可直接使用,不需要重新定義。在MFC中,也為ODBC預(yù)定義了幾個類,其中主要的是數(shù)據(jù)庫類(CDatabase)和記錄集合類(CRecoredset)。這兩個類既有聯(lián)系又有區(qū)別,在應(yīng)用程序中,可以分別使用,也可以同時使用,每一類也可以同時存在多個對象。CDatabase的每一個對象代表了一個數(shù)據(jù)源的連接,CRecordset的每一個對象代表了從一
個數(shù)據(jù)表中按預(yù)定的查詢條件獲得的記錄的集合,一般說來,前者適宜于對數(shù)據(jù)源下的某個數(shù)據(jù)表格進行整體操作,后者用于對所選的記錄集合進行處理。
同Windows類與SDK API 函數(shù)的關(guān)系一樣,CDatabase類與ODBC API函數(shù)也有類似的關(guān)系,但CDatabase類中并不包含所有的ODBC API函數(shù),大部分操作功能仍須直接調(diào)用ODBC API函數(shù),如目錄功能函數(shù),用于獲得數(shù)據(jù)源下的數(shù)據(jù)表格信息,如表格名,字段名等。
在應(yīng)用編程時,一般使用CDatabase和CRecordset的派生類。假設(shè)派生類分別為CUserdb和CUserset,而在應(yīng)用類CUserclass中,使用了一個CUserdb對象(m-db)和一個Cuserset對象(m-recset),圖2給出了用戶應(yīng)用類與ODBC類的相互關(guān)系示意圖。
@@09A05901.GIF;圖2 CDatabase CRecordset類與應(yīng)用類及數(shù)據(jù)源關(guān)系圖@@
1.m-db連接數(shù)據(jù)源
m-db在完成定義構(gòu)造后,要調(diào)用CDatabase的打開(Open)函數(shù)以進行數(shù)據(jù)源的實際連接:
m-db.Open(lpszDSN, bExclusive, bReadOnly, lpszConnect);
打開函數(shù)需要輸入四個參數(shù)。lpszDSN:要連接的數(shù)據(jù)源的名字,如果lpszDSN=NULL且lpszConnect中也沒有指明數(shù)據(jù)源名,則該調(diào)用會自動出現(xiàn)一個對話框列出所有可用的數(shù)據(jù)源(名),讓用戶選擇。bExclusive:只支持“假”(False)值,表示為共享(share)方式連接。因此,應(yīng)用程序在運行前,一定要裝入share.exe或在Windows的system.ini中裝入vshare.386。 ReadOnly:指明數(shù)據(jù)源操作方式是“只讀”還是可以修改。lpszConnect: 指明連接字符串,包括數(shù)據(jù)源名、用戶標(biāo)識碼、口令等信息。該字符串必須以“ODBC;”開頭,表示該連接是與一個ODBC數(shù)據(jù)源的連接(考慮以后版本支持非ODBC數(shù)據(jù)源)。
m-db打開后,其指針可以傳給m-recset作為其數(shù)據(jù)源。m-db關(guān)閉后,將關(guān)閉所有CRecordset對它的連接,m-db也可以重新打開。
2.m-db操作數(shù)據(jù)
數(shù)據(jù)源打開后,即可對數(shù)據(jù)庫文件中的數(shù)據(jù)表格進行操作,操作以調(diào)用SQL語句方式進行,可直接通過ODBC API函數(shù),或者CDatabase類成員函數(shù)ExecuteSQL。數(shù)據(jù)表名在SQL語句中指定,如下語句則在所在的數(shù)據(jù)源中的clerk表中插入一個記錄,記錄的name字段值為"chen"。
m-db.ExecuteSQL("insert into clerk(name) value(’chen’)");3.m-recset連接數(shù)據(jù)m-recset在構(gòu)造時,可傳入一個CDatabase對象指針,作為m-recset的數(shù)據(jù)源,當(dāng)為NULL時,必須重載CRecordset的函數(shù)GetDefaultConnect,以提供數(shù)據(jù)源連接字符串(相當(dāng)于m-db.Open中的lpszConnect)。如下則表示連接名為COMPANY的數(shù)據(jù)源(當(dāng)傳入了合法的CDatabase對象指針時,該函數(shù)將不被調(diào)用)。
CString CUserset::GetDefaultConnect()
{
return"ODBC;DSN=COMPANY;";
}4.m-recset選取記錄和字段
m-recset在調(diào)用打開函數(shù)時,即獲得了符合條件的一組記錄,條件語句在Open函數(shù)中的lpszSQL中給出,如果lpszSQL為NULL,則必須重載CRecordset的函數(shù)以提供該語句。該語句是一個SELECT語句,帶或不帶where和order by子句(如果不帶,where和Order by的條件也可在CRecordset的兩個預(yù)定義成員變量m-strFilter和m-strSort中給出)。lpszSQL也可以只是一個數(shù)據(jù)表名(table-name),也可以是對內(nèi)嵌在數(shù)據(jù)庫文件中的查詢程序的調(diào)用語句。所選擇的一系列字段名,在成員函數(shù)DoFieldExchange中由一系列RFX-函數(shù)指定。RFX-(Record Field Exchange)函數(shù),使字段和成員變量一一建立類型對應(yīng)關(guān)系。另外,m-strFilter中也可以帶變量參數(shù)(用"?"表示,如"fieldl>=? AND field2<=?"),參數(shù)與成員變量的對應(yīng)關(guān)系也在DoFieldExchange中由RFX-函數(shù)指定(串中的"?"將被參數(shù)變量值逐一替換)。
void CUserset::DoFieldExchange(CFieldExchange* pFX)
{
pFX->SetFieldType(CFieldExchange::outputColumn);
/*以下為字段連接 */
RFX-???(pFX,"field1",m-var1);
RFX-???(pFX,"field2",m-var2);
...
RFX-???(pFX,"fieldn",m-varn);
pFX->SetFieldType(CFieldExchange::param);
/*以下為參數(shù)連接*/
RFX-???(pFX,field1,m-param1);
RFX-???(pFX,field2,m-param2);
...
}其中,???為ODBC SQL數(shù)據(jù)類型名,如RFX-Double,RFX-Text等。
綜合上述,選取記錄和字段實際是由下列語句完成:
SELECT rfx-field-list FROM table-name[WHERE m-strFilter][ORDER BY m-strSort]
字段變量和參數(shù)變量的個數(shù)一定要在調(diào)用打開函數(shù)前(如構(gòu)造函數(shù)中)準(zhǔn)確地賦值給成員變量m-nFields和m-nParams。m-recset在打開后的任何時候調(diào)用Requery()函數(shù),將根據(jù)新的查詢條件(例如修改了參數(shù)變量值)重新選取記錄。
5.m-recset操作數(shù)據(jù)
記錄集合生成后,其當(dāng)前記錄的各字段值被保存在前述的各字段變量中,如果調(diào)用CRecordset的滾動(scroll)函數(shù),如MoveFirst(),MoveNext(),MovePrev(),MoveLast()等,字段變量的值將自動跟隨“當(dāng)前”記錄的位置的變化而變化。IsBOF(),IsEOF()用于判別是否移動到記錄的頭或尾。
數(shù)據(jù)操作主要包括刪除(Delete),添加(AddNew)和更改(Edit),一般流程為:
if(m-recset.CanUpdate()) /*是否允許修改*/
{
if (m-db.CanTransact()) /*是否支持“批”處理*/
{
m-db.BeginTrans();
m-recset.AddNew();
/* 修改字段變量值 */
. . .
m-recset.Update();
m-db.CommitTrans();
if(catch error)
m-db.RollBack();
}
}
對于AddNew和Edit,修改字段變量后一定要調(diào)用函數(shù)Update(),否則更新將丟失,而Delete操作則不必進行字段值修改和調(diào)用Update()。
上述的CDatabase的四個函數(shù)是ODBC為保證數(shù)據(jù)操作的可靠性而提供的“批”處理函數(shù),即在BeginTrans和CommitTrans之間的數(shù)據(jù)修改如果出現(xiàn)任何異常,可通過函數(shù)RoolBack來恢復(fù)所做的修改。
在多用戶系統(tǒng)使用時,每一個數(shù)據(jù)源可以被多個用戶的多個任務(wù)連接,不同的任務(wù)可同時修改相同的數(shù)據(jù)源。ODBC提供了兩種數(shù)據(jù)表更新的同步機制(在m-recset.Open函數(shù)中指定),“靜態(tài)”的(snapshot)和動態(tài)的(dynaset)。前者是一組靜態(tài)的記錄集合,當(dāng)建立后不會改變,除了反應(yīng)自己的添加/刪除外,不反應(yīng)別的用戶的修改,除非調(diào)用了Requery重新建立。后者是一組動態(tài)的記錄集合,自己或別的用戶所作的修改隨時反應(yīng)到集合中來(當(dāng)然也可用Requery重建),以保持記錄與數(shù)據(jù)源的同步。在應(yīng)用中,應(yīng)根據(jù)需要確定使用哪一種方式。
五、結(jié)束語
從以上討論可以看出,ODBC應(yīng)用接口十分簡便!再加上Visual C++中的AppWizard和classWizard自動生成框架代碼功能,連接一個數(shù)據(jù)源,生成一個CRecordset對象,就更快捷了。
應(yīng)用程序只需關(guān)心數(shù)據(jù)的處理而不必費心數(shù)據(jù)的存取,另外,另一個與ODBC有關(guān)的類CRecordView,是一個窗口類CWnd的派生類,建立在CRecordset上,可直接構(gòu)造數(shù)據(jù)庫記錄顯示窗口,某些情況下也不妨一用。