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