CRecordSet类
一般情况下AppWizard会在数据库应用程序中自动产生CRecordset的派生类,并将派生类和某个数据源中的表联系起来也可以和视图上的子窗口联系起来。但是有时这样做会影响到程序的灵活性,这时候我们可以单独使用CRecordSet类。利用CRecordSet类我们可以执行SQL语句,并可以读出结果集中数据。
首先我们需要包含头文件afxdb.h,可以将#include 添加到stdafx.h文件中。此外在使用CRecordset时必须有一个又一个CDatabase对象,该对象的作用是管理数据源连接。然后可以产生一个CRecordset对象,利用BOOL CRecordset::Open( UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE, LPCTSTR lpszSQL = NULL, DWORD dwOptions = none )可以执行SQL语句。
但执行成功后,可以调用以下的函数滚动光标,读取数据。
MoveFirst | 移动光标到第一条记录处 |
MoveNext | 移动光标到后一条记录处 |
MovePrev | 移动光标到前一条记录处 |
MoveLast | 移动光标到最后一条记录处 |
IsBOF | 检测光标是否在第一条记录上 |
IsEOF | 检测光标是否在最后一条记录上 |
GetFieldValue | 得到结果中数据 |
下面是具体代码:
/*
假设CDatabase m_dbConn为成员变量 假设有一个表有如下SQL语句产生:CREATE TABLE table1(loc_id not null) */ void CYourClass::ConnectToDB() {//连接数据库 BOOL fOK = m_dbConn.Open("test"); TRACE("connect fOK=%d\n",m_dbConn); } void CYourClass::Select() |
此外CRecordset::GetFieldValue有很多种原型,你可以通过指定列位置或是字段名来获取数据:
void GetFieldValue( LPCTSTR , CDBVariant& , short = DEFAULT_FIELD_TYPE ); void GetFieldValue( short , CDBVariant& , short = DEFAULT_FIELD_TYPE ); void GetFieldValue( LPCTSTR , CString& ); void GetFieldValue( short , CString& ); |
如果使用CDBVariant类型变量来获取结果,你可以得到任何类型的结果。在CDBVariant::m_dwType成员变量中记录了该变量所包含的数据类型,根据该变量的值你可以确定数据类型并引用CDBVariant对象中的相应成员变量。
CDatabase类
要建立与数据源的连接,首先应构造一个CDatabase对象,然后再调用CDatabase的Open成员函数.Open函数负责建立连接,其声明为
virtual BOOL Open( LPCTSTR lpszDSN, BOOL bExclusive = FALSE, BOOL bReadOnly = FALSE, LPCTSTR lpszConnect = “ODBC;”, BOOL bUseCursorLib = TRUE ); throw( CDBException, CMemoryException );
参数lpszDSN指定了数据源名(构造数据源的方法将在后面介绍),在lpszConnect参数中也可包括数据源名,此时lpszDSN必需为NULL,若在函数中未提供数据源名且使lpszDSN为NULL,则会显示一个数据源对话框,用户可以在该对话框中选择一个数据源.参数bExclusive说明是否独占数据源,由于目前版本的类库还不支持独占方式,故该参数的值应该是FALSE,这说明数据源是被共享的.参数bReadOnly若为TRUE则对数据源的连接是只读的.参数lpszConnect指定了一个连接字符串,连接字符串中可以包括数据源名、用户帐号(ID)和口令等信息,字符串中的"ODBC"表示要连接到一个ODBC数据源上.参数bUseCursorLib若为TRUE,则会装载光标库,否则不装载,快照需要光标库,动态集不需要光标库. 若连接成功,函数返回TRUE,若返回FALSE,则说明用户在数据源对话框中按了Cancel按钮。若函数内部出现错误,则框架会产生一个异常。
下面是一些调用Open函数的例子。
CDatabase m_db; //在文档类中嵌入一个CDatabase对象
//连接到一个名为"Student Registration"的数据源
m_db.Open("Student Registration");
//在连接数据源的同时指定了用户帐号和口令
m_db.Open(NULL,FALSE,FALSE,"ODBC;DSN=Student Registration;UID=ZYFWD=1234");
m_db.Open(NULL); //将弹出一个数据源对话框
CRecordView类
要从一个数据源中脱离,可调用函数Close。在脱离后,可以再次调用Open函数来建立一个新的连接.调用IsOpen可判断当前是否有一个连接,调用GetConnect可返回当前的连接字符串。函数的声明为
virtual void Close( );
BOOL IsOpen( ) const; //返回TRUE则表明当前有一个连接
const CString& GetConnect( ) const;
CDatabase的析构函数会调用Close,所以只要删除了CDatabase对象就可以与数据源脱离。CRecordView(记录视图)是CFormView的派生类,它提供了一个表单视图(参见6.4.1)来显示当前记录.一个典型的记录视图如图10.3所示,用户可以通过表单视图显示当前记录.通过记录视图,可以修改、添加和删除数据.用户一般需要创建一个CRecordView的派生类并在其对应的对话框模板中加入控件.
记录视图使用DDX数据交换机制在表单中的控件和记录集之间交换数据。在前面介绍的DDX都是在控件和控件父窗口的数据成员之间交换数据,而记录视图则是在控件和一个外部对象(CRecordset的派生类对象)之间交换数据.清单10.3显示了一个CRecordView的派生类的DoDataExchange函数,读者可以看出,该函数是与m_pSet指针指向的记录集对象的域数据成员交换数据的,而且,交换数据的代码是ClassWizard自动加入的.在后面的例子中,将向读者介绍用ClassWizard连接记录视图与记录集对象的方法.
清单10.3 用来与记录集对象的域数据成员交换数据的DoDataExchange函数
void CSectionForm:AdoDataExchange(CDataExchange* pDX)
{
CRecordView:AdoDataExchange(pDX);
//{{AFX_DATA_MAP(CSectionForm)
DDX_FieldText(pDX, IDC_COURSE, m_pSet->m_CourseID, m_pSet);
DDX_FieldText(pDX, IDC_SECTION, m_pSet->m_SectionNo, m_pSet);
DDX_FieldText(pDX, IDC_INSTRUCTOR, m_pSet->m_InstructorID, m_pSet);
DDX_FieldText(pDX, IDC_ROOM, m_pSet->m_RoomNo, m_pSet);
DDX_FieldText(pDX, IDC_SCHEDULE, m_pSet->m_Schedule, m_pSet);
DDX_FieldText(pDX, IDC_CAPACITY, m_pSet->m_Capacity, m_pSet);
//}}AFX_DATA_MAP
}
CRecordView本身提供了对下面四个命令的支持:
ID_RECORD_FIRST //滚动到记录集的第一个记录
ID_RECORD_LAST //滚动到记录集的最后一个记录
ID_RECORD_NEXT //前进一个记录
ID_RECORD_PREV //后退一个记录
CRecordView提供了OnMove成员函数处理这四个命令消息,OnMove函数对用户是透明的,清单10.4列出了OnMove的源代码.
清单10.4 OnMove函数
BOOL CRecordView::OnMove(UINT nIDMoveCommand)
{
CRecordset* pSet = OnGetRecordset();
if (pSet->CanUpdate())
{
pSet->Edit();
if (!UpdateData())
return TRUE;
pSet->Update();
}
switch (nIDMoveCommand)
{
case ID_RECORD_PREV:
pSet->MovePrev();
if (!pSet->IsBOF())
break;
case ID_RECORD_FIRST:
pSet->MoveFirst();
break;
case ID_RECORD_NEXT:
pSet->MoveNext();
if (!pSet->IsEOF())
break;
if (!pSet->CanScroll())
{
// clear out screen since we're sitting on EOF
pSet->SetFieldNull(NULL);
break;
}
case ID_RECORD_LAST:
pSet->MoveLast();
break;
default:
// Unexpected case value
ASSERT(FALSE);
}
// Show results of move operation
UpdateData(FALSE);
return TRUE;
}
在函数的开头先调用CRecordset::Edit进入编辑模式,接着调用UpdateData将控件中的数据更新到记录集对象的域数据成员中,然后调用CRecordset::Update将域数据成员的值写入数据源.这说明OnMove在滚动记录的同时会完成对原来记录的修改.
在函数的中间有一个分支语句用来处理四个不同的命令,在这个分支语句中调用了CRecordset的各种用于滚动记录的成员函数,这些函数在滚动到一个新的记录时会把该记录的内容设置到域数据成员中.在函数的末尾调用UpdateData(FALSE)把新的当前记录的内容设置到表单的控件中。
由此可见,OnMove一来一回完成了两次表单控件和数据源的数据交换过程.通过分析该函数,读者可以学会在浏览记录时如何控制DDX和DFX数据交换.