Visual C++ 中的ODBC編程的介紹
2020-06-27 15:41:51
供稿:網友
一.概述
ODBC 是一種使用SQL 的程序設計接口。使用ODBC 讓應用程序的編寫者避免了與數據源相聯的復雜性。這項技術目前已經得到了大多數DBMS 廠商們的廣泛支持。
Microsoft Developer Studio 為大多數標準的數據庫格式提供了32 位ODBC 驅動器。這些標準數據格式包括有:SQL Server、Access、Paradox、dBase、FoxPro、Excel、Oracle 以及Microsoft Text。如果用戶希望使用其他數據格式,用戶需要相應的ODBC 驅動器及DBMS。
用戶使用自己的DBMS 數據庫管理功能生成新的數據庫模式后,就可以使用ODBC 來登錄數據源。對用戶的應用程序來說,只要安裝有驅動程序,就能注冊很多不同的數據庫。登錄數據庫的具體操作參見有關ODBC 的聯機幫助。
二.MFC 提供的ODBC 數據庫類
Visual C++ 的MFC 基類庫定義了幾個數據庫類。在利用ODBC 編程時,經常要使用到CDatabase( 數據庫類),CRecordSet( 記錄集類) 和CRecordView( 可視記錄集類)。其中:
CDatabase 類對象提供了對數據源的連接,通過它你可以對數據源進行操作。
CRecordSet 類對象提供了從數據源中提取出的記錄集。CRecordSet 對象通常用于兩種形式:動態行集(dynasets)和快照集(snapshots)。動態行集能保持與其他用戶所做的更改保持同步??煺占瘎t是數據的一個靜態視圖。每一種形式在記錄集被打開時都提供一組記錄,所不同的是,當你在一個動態行集里滾動到一條記錄時,由其他用戶或是你應用程序中的其他記錄集對該記錄所做的更改會相應地顯示出來。
CRecordView 類對象能以控制的形式顯示數據庫記錄。這個視圖是直接連到一個CRecordSet 對象的表視圖。
三.應用ODBC 編程
應用Visual C++ 的AppWizard 可以自動生成一個ODBC 應用程序框架。方法是:打開File 菜單的New 選項,選取Projects,填入工程名,選擇MFC AppWizard (exe),然后按AppWizard 的提示進行操作。當AppWizard 詢問是否包含數據庫支持時,如果你想讀寫數據庫,那么選定Database view with file support;而 你想訪問數據庫的信息而不想回寫所做的改變,那么選定Database view without file support 選項就比較合適了。選擇了數據庫支持之后Database Source 按鈕會激活,選中它去調用Data Options 對話框。在Database Options 對話框中會顯示已向ODBC 注冊的數據庫資源,選定你所要操作的數據庫,如:Super_ES,單擊OK 后會出現Select Database Tables 對話框,其中列舉了你所選中的數據庫中包含的全部表,選擇你希望操作的表后,單擊OK。在選定了數據庫和數據表之后,你可以按照慣例繼續進行AppWizard 操作。
特別需要指出的是:在生成的應用程序框架View 類(如:CSuper_ESView)中包含一個指向CSuper_ESSet 對象的指針m_pSet,該指針由AppWizard 建立,目的是在視表單和記錄集之間建立聯系,使得記錄集中的查詢結果能夠很容易地在視表單上顯示出來。有關m_pSet 的詳細用法可以參見Visual C++ Online Book。
程序與數據語言建立聯系,使用CDatebase::OpenEx() 或CDatabase::Open() 函數來進行初始化。數據庫對象必須在你使用它構造一個記錄集對象之前被初始化。
下面舉例說明在Visual C++ 環境中ODBC 的編程技巧:
1 .查詢記錄
查詢記錄使用CRecordSet::Open() 和CRecordSet::Requery() 成員函數。在使用CRecordSet 類對象之前,必須使用CRecordSet::Open() 函數來獲得有效的記錄集。一旦已經使用過CRecordSet::Open() 函數,再次查詢時就可以應用CRecordSet::Requery() 函數。在調用CRecordSet::Open() 函數時,如果已經將一個已經打開的CDatabase 對象指針傳給CRecordSet 類對象的m_pDatabase 成員變量,則使用該數據庫對象建立ODBC 連接;否則如果m_pDatabase 為空指針,就新建一個CDatabase 類對象并使其與缺省的數據源相連,然后進行CRecordSet 類對象的初始化。缺省數據源由GetDefaultConnect() 函數獲得。你也可以提供你所需要的SQL 語句,并以它來調用CRecordSet::Open() 函數,例如:
Super_ESSet.Open(AFX_DATABASE_USE_DEFAULT,strSQL);
如果沒有指定參數,程序則使用缺省的SQL 語句,即對在GetDefaultSQL() 函數中指定的SQL 語句進行操作:
CString CSuper_ESSet::GetDefaultSQL()
{return _T("[BasicData],[MainSize]");}
對于GetDefaultSQL() 函數返回的表名,對應的缺省操作是SELECT 語句,即:
SELECT * FROM BasicData,MainSize
查詢過程中也可以利用CRecordSet 的成員變量m_strFilter 和m_strSort 來執行條件查詢和結果排序。m_strFilter 為過濾字符串,存放著SQL 語句中WHERE 后的條件串;m_strSort 為排序字符串,存放著SQL 語句中ORDER BY 后的字符串。如:
Super_ESSet.m_strFilter="TYPE=電動機";
Super_ESSet.m_strSort="VOLTAGE";
Super_ESSet.Requery();
對應的SQL語句為:
SELECT * FROM BasicData,MainSize
WHERE TYPE=電動機
ORDER BY VOLTAGE
除了直接賦值給m_strFilter 以外,還可以使用參數化。利用參數化可以更直觀,更方便地完成條件查詢任務。使用參數化的步驟如下:
(1) .聲明參變量:
CString p1;
float p2;
(2) .在構造函數中初始化參變量
p1=_T("");
p2=0.0f;
m_nParams=2;
(3) .將參變量與對應列綁定
pFX- >SetFieldType(CFieldExchange::param)
RFX_Text(pFX,_T("P1"),p1);
RFX_Single(pFX,_T("P2"),p2);
完成以上步驟之后就可以利用參變量進行條件查詢了:
m_pSet- >m_strFilter="TYPE=? AND VOLTAGE=?";
m_pSet- >p1=" 電動機";
m_pSet- >p2=60.0;
m_pSet- >Requery();
參變量的值按綁定的順序替換查詢字串中的"?" 適配符。
如果查詢的結果是多條記錄的話,可以用CRecordSet 類的函數Move(),MoveNext(),MovePrev(),MoveFirst() 和MoveLast() 來移動光標。
2 .增加記錄
增加記錄使用AddNew() 函數,要求數據庫必須是以允許增加的方式打開:
m_pSet- >AddNew(); //在表的末尾增加新記錄
m_pSet- >SetFieldNull(&(m_pSet- >m_type), FALSE);
m_pSet- >m_type=" 電動機";
... //輸入新的字段值
m_pSet- > Update(); //將新記錄存入數據庫
m_pSet- >Requery(); //重建記錄集
3 .刪除記錄
直接使用Delete() 函數,并且在調用Delete() 函數之后不需調用Update() 函數:
m_pSet- >Delete();
if (!m_pSet- >IsEOF())
m_pSet- >MoveNext();
else
m_pSet- >MoveLast();
4 .修改記錄
修改記錄使用Edit() 函數:
m_pSet- >Edit(); //修改當前記錄
m_pSet- >m_type="發電機";
//修改當前記錄字段值
...
m_pSet- >Update(); //將修改結果存入數據庫
m_pSet- >Requery();
5 .撤消操作
如果用戶選擇了增加或者修改記錄后希望放棄當前操作,可以在調用Update() 函數之前調用:
CRecordSet::Move(AFX_MOVE_REFRESH);
來撤消增加或修改模式,并恢復在增加或修改模式之前的當前記錄。其中的參數AFX_MOVE_REFRESH 的值為零。
6 .數據庫連接的復用
在CRecordSet 類中定義了一個成員變量m_pDatabase:
CDatabase* m_pDatabase;
它是指向對象數據庫類的指針。如果在CRecordSet 類對象調用Open() 函數之前,將一個已經打開的CDatabase 類對象指針傳給m_pDatabase,就能共享相同的CDatabase 類對象。如:
CDatabase m_db;
CRecordSet m_set1,m_set2;
m_db.Open(_T("Super_ES"));//建立ODBC連接
m_set1.m_pDatabase=&m_db;
//m_set1復用m_db對象
m_set2.m_pDatabse=&m_db;
// m_set2復用m_db對象
7 .SQL 語句的直接執行
雖然通過CRecordSet 類,我們可以完成大多數的查詢操作,而且在CRecordSet::Open() 函數中也可以提供SQL 語句,但是有的時候我們還想進行一些其他操作,例如建立新表,刪除表,建立新的字段等等,這時就需要使用到CDatabase 類的直接執行SQL 語句的機制。通過調用CDatabase::ExecuteSQL() 函數來完成SQL 語句的直接執行:
BOOL CDB::ExecuteSQLAndReportFailure(const CString& strSQL)
{
TRY
{
m_pdb- >ExecuteSQL(strSQL);//直接執行SQL語句
}
CATCH (CDBException,e)
{
CString strMsg;
strMsg.LoadString(IDS_EXECUTE_SQL_FAILED);
strMsg+=strSQL;
return FALSE;
}
END_CATCH
return TRUE;
}
應當指出的是,由于不同DBMS 提供的數據操作語句不盡相同,直接執行SQL 語句可能會破壞軟件的DBMS 無關性,因此在應用中應當慎用此類操作。
8 .動態連接表
表的動態連接可以利用在調用CRecordSet::Open() 函數時指定SQL 語句來實現。同一個記錄集對象只能訪問具有相同結構的表,否則查詢結果將無法與變量相對應。
void CDB::ChangeTable()
{
if (m_pSet- >IsOpen()) m_pSet- >Close();
switch (m_id)
{
case 0:
m_pSet- >Open(AFX_DB_USE_DEFAULT_TYPE,
"SELECT * FROM SLOT0"); //連接表SLOT0
m_id=1;
break;
case 1:
m_pSet- >Open(AFX_DB_USE_DEFAULT_TYPE,
"SELECT * FROM SLOT1"); //連接表SLOT1
m_id=0;
break;
}
}
9 .動態連接數據庫
由于與數據庫的連接是通過CDatabase 類對象來實現的,所以我們可以通過賦與CRecordSet 類對象參數m_pDatabase 以連接不同數據庫的CDatabase 對象指針,就可以動態連接數據庫。
void CDB::ChangeConnect()
{
CDatabase* pdb=m_pSet- >m_pDatabase;
pdb- >Close();
switch (m_id)
{
case 0:
if (!pdb- >Open(_T("Super_ES")))
//連接數據源Super_ES
{
AfxMessageBox("數據源Super_ES打開失敗,"
"請檢查相應的ODBC連接", MB_OK|MB_ICONWARNING);
exit(0);
}
m_id=1;
break;
case 1:
if (!pdb- >Open(_T("Motor")))
//連接數據源Motor
{
AfxMessageBox("數據源Motor打開失敗,"
"請檢查相應的ODBC連接", MB_OK|MB_ICONWARNING);
exit(0);
}
m_id=0;
break;
}
}
四.總結
Visual C++ 中的ODBC 類庫可以幫助程序員完成絕大多數的數據庫操作。利用ODBC 技術使得程序員從具體的DBMS 中解脫出來,從而極大的減少了軟件開發的工作量,縮短開發周期,提高了效率和軟件的可靠性。本文總結的筆者從事軟件開發的一些經驗心得希望對從事ODBC 開發的工作者有所幫助。