因为MFC完全支持数据库应用程序的开发,所以大多数数据库应用都使用CDatabase和CRecordset类,并且类向导(Class Wizard)提供了快速简易的方式来使用这两个类。有一点不足的就是当应用程序涉及到多表数据库时,类向导将产生大量的关于记录集的源码文件使得工程给人的感觉很臃肿混乱。
本文介绍如何使用一个模板记录集类来降低类向导所产生的记录集文件的数量,同时增强记录集类(CRecordset)的功能。这个模板记录集类叫做:CDataSet。它的主要目的是降低代码量,为数据对象数组提供一个接口。
CDataSet类定义如下:
01.CDataSet
头文件02.template class CDataSet
: public CRecordset03.{04.public:05.CDataSet(LPCSTR Table,
CDatabase* pdb);06.T
m_Data; //
Attached object07.CString
m_DefaultSQL; //
Default SQL SELECT statement08.CString
m_DefaultSort; //
Default SQL ORDER BY clause09.CString
m_DefaultFilter; //
Default SQL WHERE clause10. 11.//
Operations12.public:13.virtual BOOL Search(LPCSTR Filter, LPCSTR Sort
= NULL, BOOL bFail
= FALSE);14.virtual BOOL DirectSearch(LPCSTR Filter, LPCSTR Sort
= NULL, BOOL bFail
= FALSE);15.virtual void LoadAll(CArray&
A, int N
= 0);16.virtual void SaveAll(CArray&
A);17.virtual void Load(T&
Data) { Data = m_Data; }18.virtual void Store(T&
Data);19. 20.//
Implementation21.protected:22.virtual CString
GetDefaultSQL() { return m_DefaultSQL;
}23.virtual void DoFieldExchange(CFieldExchange*
pFX);24.};这个模板有两个参数:一个是数据对象类,另一个是绑定字段的数量。数据对象类由一组成员变量组成,对应着不同的数据库表的字段,例如:
01.struct DataObj
{02.CString
Var1;03.CString
Var2;04.int Var3;05.float Var4;06. 07.//
......08. 09.//
成员函数10.};如果将数据对象定义成一个类,而不是一个结构,就必须定义缺省的供类模板使用的构造函数并重载 “=”操作符(operator=),要不然的话编译会出错。CDataSet的成员变量m_DefaultSQL用来灵活方便地控制SQL数据源,m_DefaultSQL可以是一个表名、表名的列表、或者是任何复杂的SQL语句,还有就是因为m_DefaultSQL可以在运行时被改变,使得相同的类访问不同的表(如果这些表有相同的结构)成为可能。如果默认的SQL发生变化,相应的记录集必须被重新打开。
Load(), Store(), LoadAll() 和 StoreAll() 方法完成单个或多个数据对象的加载和存储操作。LoadAll()的最后一个参数,N,指定要加载记录数的最大值。Search()和 DirectSearch()方法实现改进的搜索能力。Search()使用m_DefaultSort 和 m_DefaultFilter成员变量并且当希望的记录未找到和 bFail为TRUE时丢出异常。DirectSearch()接受一个外部指定的SQL WHERE 从句。以下是这个模板类的实现,它不是很复杂:
CDataSet实现
01.template CDataSet::CDataSet(LPCSTR Table,
CDatabase* pdb) :02.CRecordset(pdb)03.{04.m_nFields
= M;05.m_DefaultSQL
= Table;06.m_DefaultFilter
= "%s";07.}08. 09.template BOOL CDataSet::Search(LPCSTR Filter,LPCSTR Sort, BOOL bFail)10.{11.if (
IsOpen() )12.Close();13. 14.SetStatus("Opening
" +
m_DefaultSQL + "
...");15. 16.if (
Filter )17.m_strFilter.Format(m_DefaultFilter,
Filter);18.else19.m_strFilter
= "";20.m_strSort
= Sort;21. 22.Open();23. 24.//
Throw exception if record not found25.if (
bFail && IsEOF() )26.THROW(new CMyException(m_DefaultSQL
+ "
record not found!"));27.return !IsEOF();28.}29. 30.template BOOL CDataSet::DirectSearch(LPCSTR Filter, LPCSTR Sort, BOOL bFail)31.{32.if (
IsOpen() )33.Close();34. 35.SetStatus("Opening
" +
m_DefaultSQL + "
...");36. 37.m_strFilter
= Filter;38.m_strSort
= Sort;39.Open();40. 41.//
Throw exception if record not found42.if (
bFail && IsEOF() )43.THROW(new CMyException(m_DefaultSQL
+ "
record not found!"));44.return !IsEOF();45.}46. 47.template void CDataSet::LoadAll(CArray&
A, int N)48.{49.SetStatus("Loading
" +
m_DefaultSQL + "
...");50.A.RemoveAll();51. 52.while (
!IsEOF() && (N == 0 || A.GetSize() < N) )53.{54.A.Add(m_Data);55.MoveNext();56.} 57.}58. 59.template void CDataSet::SaveAll(CArray&
A)60.{61.SetStatus("Writing
" +
m_DefaultSQL + "
...");62. 63.for ( int i
= 0;i < A.GetSize(); i++ )64.{65.AddNew();66.Store(A[i]);67.} 68.}69. 70.template void CDataSet::Store(T&
Data)71.{72.SetStatus("Updating
" +
m_DefaultSQL + "
...");73.Edit();74.m_Data
= Data;75.Update();76.}77. 78.template void CDataSet::Close()79.{80.CRecordset::Close();81.SetStatus("Ready");82.}CDataSet类中许多方法都使用SetStatus()函数,它的作用是显示一个沙漏以及在应用程序状态条显示当前的操作状态。
01.void SetStatus(const CString
Msg)02.{03.CFrameWnd*
pMainFrame = (CFrameWnd*)AfxGetMainWnd();04. 05.if (
pMainFrame )06.{07.pMainFrame->SetMessageText(Msg);08.pMainFrame->UpdateWindow();09. 10.if ( strcmp(Msg, "Ready")
== 0 )11.pMainFrame->EndWaitCursor();12.else13.pMainFrame->BeginWaitCursor();14.}15.}
当处理大量数据和执行复杂的查询时,这个函数特别有用。它告诉用户应用程序正在处理数据.
CMyException是一个简单的异常处理类,如果运行出错,通过这个类来丢出异常,并显示指定的错误信息。
以下使这个类的定义和实现:
01.class CMyException: public CException
{02.CString
m_ErrorMsg;03.public:04.CMyException(int ErrMsgResourceID);05.CMyException(CString
ErrMsg);06.BOOL GetErrorMessage(LPTSTR lpszError,?07.UINT nMaxError,08.PUINT pnHelpContext
= NULL);09.};10. 11.CMyException::CMyException(int ResourceID)12.{13.m_ErrorMsg.LoadString(ResourceID);14.}15. 16.CMyException::CMyException(CString
ErrorMsg)17.{18.m_ErrorMsg
= ErrorMsg;19.}20. 21.BOOL CMyException::GetErrorMessage(LPTSTR lpszError,22.UINT nMaxError,23.PUINT)24.{25.strncpy(lpszError,
(LPCSTR)m_ErrorMsg,
nMaxError);26.return TRUE;27.}在使用CDataSet类的应用程序中,对象可以被实例化如下:
1.CDataset
MySet("Table1",
&db);这里“Table1”是数据库表的名字,“&db”指向一个打开的数据库,在大多数情况下(尤其是当使用事务处理时),实例创建数据库对象并且在打开记录机之前从外部打开数据库。
您是否注意到在Listing 2 的代码中有一件事情没有做?,对每一个数据类而言必须要单独实现DoFieldExchange()来建立一个数据对象成员和数据库字段之间的联接,Listing 3 中的代码告诉您如何为DataObj实现DoFieldExchange()。
数据对象的 DoFieldExchange() 方法
1.void CDataSet::DoFieldExchange(CFieldExchange*
pFX)2.{3.pFX->SetFieldType(CFieldExchange::outputColumn);4.RFX_Text(pFX, "VAR1",
m_Data.Var1);5.RFX_Text(pFX, "VAR2",
m_Data.Var2);6.RFX_Int(pFX, "VAR3",
m_Data.Var3);7.RFX_Single(pFX, "VAR4",
m_Data.Var4);8.}以下是使用CDataSet的一个例子:
01.CDatabase
db;02.CArray
A;03. 04.TRY
{05.db.Open("TEST");06. 07.//
Create recordset object08.CDataSet
MySet("Table1",
&db);09. 10.//
Set default filter to var111.MySet.m_DefaultFilter
= "Var1
= ''%s''";12.MySet.Open();13. 14.//
Load all of the records15.MySet.LoadAll(A);16. 17.//
Find some record18.if (
MySet.Search("Anything")
)19.{20.DataObj
B;21. 22.//
Load, update, and store new record23.MySet.Load(B);24.B.Var1
= "Anything
else?";25.MySet.Store(B);26.}27. 28.MySet.Close();29.AfxMessageBox("Data
is loaded!");30.}31.CATCH_ALL(e)
{32.e->ReportError();33.}34.END_CATCH_ALL为了测试例子,要创建一个系统数据源“Test”(MS Access Driver),指向Test.mdb,运行DbTest.exe,在菜单中选择 Test =〉DataSet。
本文介绍了一种使用模板记录集类CDataSet优化MFC数据库应用程序的方法,该方法减少了记录集文件的数量并增强了CRecordset类的功能。CDataSet通过提供数据对象数组接口降低了代码量。

1万+

被折叠的 条评论
为什么被折叠?



