王朝网络
分享
 
 
 

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

王朝vc·作者佚名  2006-01-17
宽屏版  字体: |||超大  

介绍一个增强的数据库类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;

// ......

// 成员函数

};

如果将数据对象定义成一个类,而不是一个结构,就必须定义缺省的供类模板使用的构造函数并重载 “=”

操作符(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实现

template<class T, int M> CDataSet<T, M>::CDataSet(LPCSTR Table, CDatabase* pdb) :

CRecordset(pdb)

{

m_nFields = M;

m_DefaultSQL = Table;

m_DefaultFilter = "%s";

}

template<class T, int M> BOOL CDataSet<T, M>::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<class T, int M> BOOL CDataSet<T, M>::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<class T, int M> void CDataSet<T, M>::LoadAll(CArray<T, T>& A, int N)

{

SetStatus("Loading " + m_DefaultSQL + " ...");

A.RemoveAll();

while ( !IsEOF() && (N == 0 || A.GetSize() < N) )

{

A.Add(m_Data);

MoveNext();

}

}

template<class T, int M> void CDataSet<T, M>::SaveAll(CArray<T, T>& A)

{

SetStatus("Writing " + m_DefaultSQL + " ...");

for ( int i = 0;i < A.GetSize(); i++ )

{

AddNew();

Store(A[i]);

}

}

template<class T, int M> void CDataSet<T, M>::Store(T& Data)

{

SetStatus("Updating " + m_DefaultSQL + " ...");

Edit();

m_Data = Data;

Update();

}

template<class T, int M> void CDataSet<T, M>::Close()

{

CRecordset::Close();

SetStatus("Ready");

}

CDataSet类中许多方法都使用SetStatus()函数,它的作用是显示一个沙漏以及在应用程序状

态条显示当前的操作状态。

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();

}

}

当处理大量数据和执行复杂的查询时,这个函数特别有用。它告诉用户应用程序正在处理数据。

CMyException是一个简单的异常处理类,如果运行出错,通过这个类来丢出异常,并显示指定的错误信息。以下使这个类的定义和实现:

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<DataObj, 4> MySet("Table1", &db);

这里“Table1”是数据库表的名字,“&db”指向一个打开的数据库,在大多数情况下(尤其是当使用事务处理时),实例创建数据库对象并且在打开记录机之前从外部打开数据库。

您是否注意到在Listing 2 的代码中有一件事情没有做?,对每一个数据类而言必须要单独实现DoFieldExchange()来建立一个数据对象成员和数据库字段之间的联接,Listing 3 中的代码告诉您如何为DataObj实现DoFieldExchange()。

数据对象的 DoFieldExchange() 方法

void CDataSet<DataObj, 4>::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);

}

以下是使用CDataSet的一个例子:

CDatabase db;

CArray<DataObj, DataObj> A;

TRY {

db.Open("TEST");

// Create recordset object

CDataSet<DataObj, 4> 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

为了测试例子,要创建一个系统数据源“Test”(MS Access Driver),指向Test.mdb,运行DbTest.exe,在菜单中选择

Test =〉DataSet。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有