分类: C/C++
2008-04-23 21:40:42
介绍一个增强的数据库类CDataSet
编译/
因为MFC完全支持数据库应用程序的开发,所以大多数数据库应用都使用CDatabase和CRecordset类,并且类向导(Class
Wizard)提供了快速简易的方式来使用这两个类。有一点不足的就是当应用程序涉及到多表数据库时,类向导将产生大量的关于记录集的源码文件使得工程给人的感觉很臃肿混乱。
本文介绍如何使用一个模板记录集类来降低类向导所产生的记录集文件的数量,同时增强记录集类(CRecordset)的功能。这个模板记录集类叫做:CDataSet。它的主要目的是降低代码量,为数据对象数组提供一个接口。
CDataSet类定义如下:
CDataSet 头文件 template这个模板有两个参数:一个是数据对象类,另一个是绑定字段的数量。数据对象类由一组成员变量组成,对应着不同的数据库表的字段,例如:class CDataSet : public CRecordset { public: CDataSet(LPCSTR Table, CDatabase* pdb); T m_Data; // Attached object CString m_DefaultSQL; // Default SQL SELECT statement CString m_DefaultSort; // Default SQL ORDER BY clause CString m_DefaultFilter; // Default SQL WHERE clause // Operations public: virtual BOOL Search(LPCSTR Filter, LPCSTR Sort = NULL, BOOL bFail = FALSE); virtual BOOL DirectSearch(LPCSTR Filter, LPCSTR Sort = NULL, BOOL bFail = FALSE); virtual void LoadAll(CArray & A, int N = 0); virtual void SaveAll(CArray & A); virtual void Load(T& Data) { Data = m_Data; } virtual void Store(T& Data); // Implementation protected: virtual CString GetDefaultSQL() { return m_DefaultSQL; } virtual void DoFieldExchange(CFieldExchange* pFX); };
struct DataObj { CString Var1; CString Var2; int Var3; float Var4; // ...... // 成员函数 };如果将数据对象定义成一个类,而不是一个结构,就必须定义缺省的供类模板使用的构造函数并
CDataSet类中许多方法都使用SetStatus()函数,它的作用是显示一个沙漏以及在应用程序状 态条显示当前的操作状态。CDataSet实现 template CDataSet ::CDataSet(LPCSTR Table, CDatabase* pdb) : CRecordset(pdb) { m_nFields = M; m_DefaultSQL = Table; m_DefaultFilter = "%s"; } template BOOL CDataSet ::Search(LPCSTR Filter,LPCSTR Sort, BOOL bFail) { if ( IsOpen() ) Close(); SetStatus("Opening " m_DefaultSQL " ..."); if ( Filter ) m_strFilter.Format(m_DefaultFilter, Filter); else m_strFilter = ""; m_strSort = Sort; Open(); // Throw exception if record not found if ( bFail && IsEOF() ) THROW(new CMyException(m_DefaultSQL " record not found!")); return !IsEOF(); } template BOOL CDataSet ::DirectSearch(LPCSTR Filter, LPCSTR Sort, BOOL bFail) { if ( IsOpen() ) Close(); SetStatus("Opening " m_DefaultSQL " ..."); m_strFilter = Filter; m_strSort = Sort; Open(); // Throw exception if record not found if ( bFail && IsEOF() ) THROW(new CMyException(m_DefaultSQL " record not found!")); return !IsEOF(); } template void CDataSet ::LoadAll(CArray & A, int N) { SetStatus("Loading " m_DefaultSQL " ..."); A.RemoveAll(); while ( !IsEOF() && (N == 0 || A.GetSize() < N) ) { A.Add(m_Data); MoveNext(); } } template void CDataSet ::SaveAll(CArray & A) { SetStatus("Writing " m_DefaultSQL " ..."); for ( int i = 0;i < A.GetSize(); i ) { AddNew(); Store(A[i]); } } template void CDataSet ::Store(T& Data) { SetStatus("Updating " m_DefaultSQL " ..."); Edit(); m_Data = Data; Update(); } template void CDataSet ::Close() { CRecordset::Close(); SetStatus("Ready"); }
void SetStatus(const CString Msg) { CFrameWnd* pMainFrame = (CFrameWnd*)AfxGetMainWnd(); if ( pMainFrame ) { pMainFrame->SetMessageText(Msg); pMainFrame->UpdateWindow(); if ( strcmp(Msg, "Ready") == 0 ) pMainFrame->EndWaitCursor(); else pMainFrame->BeginWaitCursor(); } }当处理大量数据和执行复杂的查询时,这个函数特别有用。它告诉用户应用程序正在处理数据。
class CMyException: public CException { CString m_ErrorMsg; public: CMyException(int ErrMsgResourceID); CMyException(CString ErrMsg); BOOL GetErrorMessage(LPTSTR lpszError,? UINT nMaxError, PUINT pnHelpContext = NULL); }; CMyException::CMyException(int ResourceID) { m_ErrorMsg.LoadString(ResourceID); } CMyException::CMyException(CString ErrorMsg) { m_ErrorMsg = ErrorMsg; } BOOL CMyException::GetErrorMessage(LPTSTR lpszError, UINT nMaxError, PUINT) { strncpy(lpszError, (LPCSTR)m_ErrorMsg, nMaxError); return TRUE; }在使用CDataSet类的应用程序中,对象可以被实例化如下:
CDataset这里“Table1”是数据库表的名字,“&db”指向一个打开的数据库,在大多数情况下(尤其是当使用事务处理时),实例创建数据库对象并且在打开记录机之前从外部打开数据库。 您是否注意到在Listing 2 的代码中有一件事情没有做?,对每一个数据类而言必须要单独实现DoFieldExchange()来建立一个数据对象成员和数据库字段之间的联接,Listing 3 中的代码告诉您如何为DataObj实现DoFieldExchange()。MySet("Table1", &db);
数据对象的 DoFieldExchange() 方法 void CDataSet以下是使用CDataSet的一个例子:::DoFieldExchange(CFieldExchange* pFX) { pFX->SetFieldType(CFieldExchange::outputColumn); RFX_Text(pFX, "VAR1", m_Data.Var1); RFX_Text(pFX, "VAR2", m_Data.Var2); RFX_Int(pFX, "VAR3", m_Data.Var3); RFX_Single(pFX, "VAR4", m_Data.Var4); }
CDatabase db; CArray为了测试例子,要创建一个系统数据源“Test”(MS Access Driver),指向Test.mdb,运行DbTest.exe,在菜单中选择 Test =〉DataSet。A; TRY { db.Open("TEST"); // Create recordset object CDataSet MySet("Table1", &db); // Set default filter to var1 MySet.m_DefaultFilter = "Var1 = ''%s''"; MySet.Open(); // Load all of the records MySet.LoadAll(A); // Find some record if ( MySet.Search("Anything") ) { DataObj B; // Load, update, and store new record MySet.Load(B); B.Var1 = "Anything else?"; MySet.Store(B); } MySet.Close(); AfxMessageBox("Data is loaded!"); } CATCH_ALL(e) { e->ReportError(); } END_CATCH_ALL