介绍一个增强的数据库类CDataSet

本文介绍了一种使用模板记录集类CDataSet优化MFC数据库应用程序的方法,该方法减少了记录集文件的数量并增强了CRecordset类的功能。CDataSet通过提供数据对象数组接口降低了代码量。

因为MFC完全支持数据库应用程序的开发,所以大多数数据库应用都使用CDatabase和CRecordset类,并且类向导(Class Wizard)提供了快速简易的方式来使用这两个类。有一点不足的就是当应用程序涉及到多表数据库时,类向导将产生大量的关于记录集的源码文件使得工程给人的感觉很臃肿混乱。 

本文介绍如何使用一个模板记录集类来降低类向导所产生的记录集文件的数量,同时增强记录集类(CRecordset)的功能。这个模板记录集类叫做:CDataSet。它的主要目的是降低代码量,为数据对象数组提供一个接口。

CDataSet类定义如下:

01.CDataSet 头文件
02.template class CDataSet : public CRecordset
03.{
04.public:
05.CDataSet(LPCSTR Table, CDatabase* pdb);
06.T               m_Data;          // Attached object
07.CString         m_DefaultSQL;    // Default SQL SELECT statement
08.CString         m_DefaultSort;   // Default SQL ORDER BY clause
09.CString         m_DefaultFilter; // Default SQL WHERE clause
10. 
11.// Operations
12.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.// Implementation
21.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.else
19.m_strFilter = "";
20.m_strSort = Sort;
21. 
22.Open();
23. 
24.// Throw exception if record not found
25.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 found
42.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.else
13.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 object
08.CDataSet MySet("Table1", &db);
09. 
10.// Set default filter to var1
11.MySet.m_DefaultFilter = "Var1 = ''%s''";
12.MySet.Open();
13. 
14.// Load all of the records
15.MySet.LoadAll(A);
16. 
17.// Find some record
18.if ( MySet.Search("Anything") )
19.{
20.DataObj B;
21. 
22.// Load, update, and store new record
23.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。 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值