第 9章数据库编程
9.1 数据库概述
9.2 ODBC数据库管理
9.3 数据库相关的 ActiveX控件
9.1数据库概述
数据库和 DBMS
指以一定的组织形式存放在计算机存储介质上的相互关联的数据的集合。
为了有效地管理数据库,常常需要一些数据库管理系统 (DBMS)提供对数据库操作的各种命令、工具及方法。
SQL
SQL语句分为两类:一是 DDL语句,用来创建表、索引等,另一是 DML,这些语句是用来读取数据、更新数据和执行其他类似操作的语句。
ODBC,DAO和 OLE DB
ODBC提供了应用程序接口 (API),使得任何一个数据库都可以通过 ODBC驱动器与指定的 DBMS相联。程序可以通过调用 ODBC驱动管理器中相应的驱动程序达到管理数据库的目的。
DAO使用 Jet数据库引擎形成一系列的数据访问对象:数据库对象、表和查询对象、记录集对象等。可以打开一个 Access数据库文件 (MDB文件 ),也可直接打开一个 ODBC数据源以及使用 Jet引擎打开一个 ISAM(被索引的顺序访问方法 )类型的数据源 (dBASE,FoxPro,Paradox,Excel或文本文件 )。
OLE DB试图提供一种统一的数据访问接口,并能处理除了标准的关系型数据库中的数据之外,还能处理包括邮件数据,Web上的文本或图形、目录服务 (Directory Services)以及主机系统中的 IMS和 VSAM数据。 OLE DB提供一个数据库编程 COM(组件对象模型 )接口,使得数据的使用者 (应用程序 )可以使用同样的方法访问各种数据,而不用考虑数据的具体存储地点、格式或类型。这个 COM接口与 ODBC相比,其健壮性和灵活性要高得多。但是,由于
OLE DB的程序比较复杂,因而对于一般用户来说使用 ODBC和 DAO方式已能满足一般数据库处理的需要。
9.2ODBC数据库管理
9.2.1MFC的 ODBC过程
设计数据库
9.2ODBC数据库管理
定义 ODBC的数据源图 9.1 Windows 98的“控制面板” 图 9.2 ODBC数据源管理器
9.2ODBC数据库管理定义用户的 DSN的过程如下。
(1)单 击 [添加 ]按钮,如图。
(2)在该对话框中选,Microsoft Access Driver”。单击 [完成 ],如图。
(3)在对话框中,单击 [选取 ]按钮将前面创建的数据库调入。
(4)单击 [确定 ],如图。
图 9.3,创建新数据源”对话框图 9.4 ODBC Access 安装对话框图 9.5 用户数据源列表
9.2ODBC数据库管理
MFC Appwizard的创建
(1)用 MFC AppWizard(exe)创建一个单文档应用程序 Ex_ODBC。
(2)在向导的第二步对话框中加入数据库的支持,如图。
(3)选中,Database view with file support”项,单击 [Data Source],如图。
图 9.6 向导的第二步对话框图 9.7,Database Options”对话框
9.2ODBC数据库管理
(4)选择 ODBC的数据源,My database for VC”,单击 [OK],如图,从中选择要使用的表 (这里选择 xs)。
(5)单击 [OK],回到向导的第二步对话框。单击 [Finish]。
(6)编译并运行,如图。
图 9.8,Select Database Tables”对话框图 9.9 Ex_ODBC运行结果记录浏览按钮
9.2ODBC数据库管理
浏览记录
(1)将前面的单文档项目 Ex_ODBC调入。
(2)切换到项目工作区窗口的 ResourceView页面,打开用于表单视图
CEx_ODBCView的对话框资源 IDD_EX_ODBC_FORM。
(3)参看图 9.10向对话框中添加下列控件。
图 9.10 控件的设计
9.2ODBC数据库管理
(4)选择,View”菜单?,Class Wizard”或按快捷键 Ctrl+W,切换到 Member
Variables页面,在 Class name框中选择 CEx_ODBCView,为上述控件添加相关联的数据成员。例如,双击 IDC_PROF_CODE,在弹出的对话框中的成员变量下拉列表中选择要添加的成员变量名 m_pSet->m_profcode,如图。
(5)按照上一步骤的方法,为下表的控件依次添加相关联的数据成员。
图 9.11 为控件添加数据成员
9.2ODBC数据库管理
(6)编译并运行,如图。
图 9.12 Ex_ODBC浏览记录
9.2ODBC数据库管理
改变与 m_pSet关联的表
(1)将 ClassWizard对话框切换到,Member Variables”页面,在,Class
name”的下拉列表中选择,CEx_ODBCSet”,此时 ClassWizard对话框的
[Update Columns和 [Bind All按钮被激活。如图。
图 9.13,MFC ClassWizard”对话框
9.2ODBC数据库管理
(2)单击 [Update Columns],如图。
(3)从中选择 ODBC的数据源,My database for VC”,单击 [OK],如图,选择要使用的表。
(4)单击 [OK]按钮,以回到 ClassWizard界面。
(5)若表中的各个字段还没有相应的数据成员,可单击 [Bind All]按钮进行自动设置。
图 9.14,Database Options”对话框 图 9.15,Select Database Tables”对话框
9.2ODBC数据库管理
9.2.2MFC的 ODBC类
动态行集和快照集
CRecordset类对象提供了从数据源中提取出表的记录集,并提供了两种操作形式:动态行集 (Dynasets)和快照集 (Snapshots)。
动态行集能与其他用户所做的更改保持同步,快照集是数据的一个静态视图。这两形式在记录集被打开时都提供一组记录,所不同的是:当在一个动态行集里滚动一条记录时,由其他用户或应用程序中的其他记录集对该记录所做的更改会相应地显示出来,而快照集则不会。
CRecordset类的基本操作
(1)查询记录使用 CRecordset::Open和 CRecordset::Requery成员函数可以对表进行记录的查询。注意:
在使用 CRecordset类对象之前,必须使用 CRecordset::Open函数来获得有效的记录集。一旦已经使用过 CRecordset::Open函数,再次查询时就需要应用 CRecordset:,Requery()函数进行记录的刷新。
查询过程中,通常利用 CRecordset的成员变量 m_strFilter和 m_strSort来执行条件查询和结果排序。 m_strFilter为过滤字符串,存放着 SQL语句中
WHERE后的条件串;而 m_strSort为排序字符串,存放着 SQL语句中 ORDER
BY后的字符串。
9.2ODBC数据库管理
(2)增加记录增加记录是使用 AddNew函数,要求数据库必须是以,可增加,的方式打开的。在表的末尾增加新记录:
m_pSet->AddNew(); // 在表的末尾增加新记录
m_pSet->SetFieldNull(&(m_pSet->m_name),FALSE);// 设定 m_name字段值不为空
(NULL)
m_pSet->m_name="李 林 ";
.....,// 输入新的字段值
m_pSet->Update(); // 将新记录存入数据库
m_pSet->Requery(); // 刷新记录集,这在快照集方式下是必须的把一个记录加到表中时,应该调用 CRecordset::MoveLast。
(3)删除记录可以直接使用 Delete函数来删除记录,并且在调用 Delete()函数之后不需调用 Update()
函数,但要移动当前记录位置以使删除有效。例如:
CRecordsetStatus status;
m_pSet->GetStatus(status);
m_pSet->Delete();
if (status.m_lCurrentRecord==0)
m_pSet->MoveNext(); // 下移一个记录
else
m_pSet->MoveFirst(); // 移动到第一个记录处
UpdateData(FALSE);
9.2ODBC数据库管理
(4)修改记录函数 Edit可以用来修改记录,例如:
m_pSet->Edit(); // 修改当前记录
m_pSet->m_name="刘向东 "; // 修改当前记录字段值
......
m_pSet->Update(); // 将修改结果存入数据库
m_pSet->Requery();
(5)撤消操作如果在进行增加或者修改记录后,希望放弃当前操作,则可以在调用
Update()函数之前调用 CRecordset::Move(AFX_MOVE_REFRESH)来撤消增加或修改操作,并恢复在增加或修改模式之前的当前记录。
9.2ODBC数据库管理
9.2.3数据库编程
显示记录总数和当前记录号
GetRecordCount和 GetStatus用来获得表中的记录总数和当前记录的索引,
原型:
long GetRecordCount( ) const;
void GetStatus( CRecordsetStatus& rStatus ) const;
参数 rStatus是指向下列的 CRecordsetStatus结构的对象:
struct CRecordsetStatus
{ long m_lCurrentRecord; // 当前记录的索引,0表示第一个记录,
// 1表示第二个记录,依次类推。但 -1表示在第
// 一个记录之前,-2表示不确定。
BOOL m_bRecordCountFinal; // 记录总数是否是最终结果
};
注意,GetRecordCount函数所返回的记录总数在表打开时或调用 Requery函数后是不确定的,必须经过下列的代码才能获得最终有效的记录总数:
while (!m_pSet->IsEOF())
{ m_pSet->MoveNext();
m_pSet->GetRecordCount();
}
9.2ODBC数据库管理
[例 Ex_ODBC] 显示记录信息。
(1)将前面的单文档应用程序 Ex_ODBC调入。
(2)打开 MainFrm.cpp文件,将 indicators数组修改如下:
static UINT indicators[] =
{ ID_SEPARATOR,// 第一个信息行窗格
ID_SEPARATOR,// 第二个信息行窗格
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,};
(3) 为 CEx_ODBCView类添加 OnCommand消息处理函数,增加代码。
BOOL CEx_ODBCView::OnCommand(WPARAM wParam,LPARAM lParam)
{ CString str;
CMainFrame* pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;
CStatusBar* pStatus = &pFrame->m_wndStatusBar;
if (pStatus)
{ CRecordsetStatus rStatus;
m_pSet->GetStatus(rStatus); // 获得当前记录信息
str.Format("当前记录,%d/总记录,%d",1+rStatus.m_lCurrentRecord,
m_pSet->GetRecordCount());
pStatus->SetPaneText(1,str); // 更新第二个窗格的文本
}
return CRecordView::OnCommand(wParam,lParam);
}
9.2ODBC数据库管理
(4)在 CEx_ODBCView的 OnInitialUpdate函数处添加下列代码:
void CEx_ODBCView::OnInitialUpdate()
{ m_pSet = &GetDocument()->m_ex_ODBCSet;
CRecordView::OnInitialUpdate(); // 视图更新并初始化
GetParentFrame()->RecalcLayout();
ResizeParentToFit(); // 根据视图的尺寸重新调整父窗口的大小
while (!m_pSet->IsEOF())
{ m_pSet->MoveNext();
m_pSet->GetRecordCount();
}
m_pSet->MoveFirst();
}
(5)在 Ex_ODBCView.cpp文件的开始处增加下列语句:
#include,MainFrm.h”
(6)将 MainFrm.h文件中的保护型变量 m_wndStatusBar变成公共变量。
9.2ODBC数据库管理
(7)编译并运行,如图。
图 9.16 显示记录信息显示的记录信息
9.2ODBC数据库管理
编辑记录
(1)理解,删除,
CRecordset类的成员函数 Delete只是将记录进行,逻辑,删除,而不是,物理,删除。逻辑删除的记录还可以恢复,而物理删除则不能。
(2)控件与字段数据成员的相互影响在 MFC创建的数据库处理的应用程序框架中,表的字段总是和系统定义的缺省数据成员相关联。在表单视图 CEx_ODBCView中添加一些控件用于记录的浏览,其中控件 IDC_STU_ID的成员变量也是 CEx_ODBCSet的 m_stuid,
控件数据成员与字段数据成员必然相互影响。合理利用这些影响能简化编程,例如下面的代码是用来增加记录的:
m_pSet->AddNew(); //在表的末尾增加新记录
UpdateData(TRUE); // 将控件中的数据传给字段数据成员
m_pSet->Update(); // 将新记录存入数据库
m_pSet->MoveLast(); // 将当前记录位置定位到最后一个记录
UpdateData(FALSE); // 将字段数据成员的数据传给控件,即在控件中显示
9.2ODBC数据库管理
[例 Ex_ODBC] 在表单视图中增加三个按钮,[添加记录 ],[修改记录 ]和 [删除记录 ],如图。单击 [添加记录 ]或 [修改记录 ]将弹出一个如图所示的对话框,
在对话框中可以进行数据的添加或修改,单击对话框的 [确定 ]则数据有效。
图 9.17 Ex_ODBC的记录编辑 图 9.18,学生表”对话框
9.2ODBC数据库管理
(1)将前面的单文档应用项目 Ex_ODBC调入。
(2)切换到项目工作区窗口的 ResourceView页面,打开用于表单视图
CEx_ODBCView的对话框资源 IDD_EX_ODBC_FORM。看图 9.17,向表单中添加三个按钮,[添加记录 ],[修改记录 ]和 [删除记录 ]。
(3)添加一个对话框资源,打开属性对话框将其字体设置。
(4)参看图 9.18,将表单中的控件复制到对话框中,并将 [OK]和 [Cancel]按钮的标题分别改为,确 定,和,取 消,。图中具有 3D效果的竖直线是用静态图片控件 (属性为 Frame,Etched)构造的。双击对话框模板或按 Ctrl+W快捷键,为对话框资源 IDD_STU_TABLE创建一个对话框类 CStuDlg。
(5)打开 ClassWizard的 Member Variables标签,在 Class name中选择
CStuDlg,选中所需的控件 ID号,双击鼠标或单击 Add Variables按钮。依次为下列控件增加成员变量。
9.2ODBC数据库管理
(6)切换到 ClassWizard的 Messsage Maps标签页,为 CStuDlg中的控件 IDOK
增加 BN_CLICKED的消息映射,并添加下列代码:
void CStuDlg::OnOK()
{ UpdateData(TRUE);
CDialog::OnOK();
}
(7)用 ClassWizard为 CEx_ODBCView类中的三个按钮,IDC_REC_ADD、
IDC_REC_EDIT和 IDC_REC_DEL增加 BN_CLICKED的消息映射,添加代码:
void CEx_ODBCView::OnRecAdd()
{ CStuDlg dlg;
if (dlg.DoModal()==IDOK){
m_pSet->AddNew();
m_pSet->m_stuid=dlg.m_StuID;
m_pSet->m_name=dlg.m_StuName;
m_pSet->m_sex=dlg.m_StuSex;
m_pSet->m_profcode=dlg.m_ProfCode;
m_pSet->Update();
m_pSet->Requery();
}
}
9.2ODBC数据库管理
void CEx_ODBCView::OnRecEdit()
{ CStuDlg dlg;
dlg.m_StuID=m_pSet->m_stuid;
dlg.m_StuName=m_pSet->m_name;
dlg.m_StuSex=m_pSet->m_sex;
dlg.m_ProfCode=m_pSet->m_profcode;
if (dlg.DoModal()==IDOK){
m_pSet->Edit();
m_pSet->m_stuid=dlg.m_StuID;
m_pSet->m_name=dlg.m_StuName;
m_pSet->m_sex=dlg.m_StuSex;
m_pSet->m_profcode=dlg.m_ProfCode;
m_pSet->Update();
UpdateData(FALSE);
}
}
void CEx_ODBCView::OnRecDel()
{ CRecordsetStatus status;
m_pSet->GetStatus(status);
m_pSet->Delete();
if (status.m_lCurrentRecord==0) m_pSet->MoveNext();
else m_pSet->MoveFirst();
UpdateData(FALSE);
}
9.2ODBC数据库管理
(8)在 Ex_ODBCView.cpp文件的开始处增加下列语句:
#include,StuDlg.h”
(9)编译并运行。
9.2ODBC数据库管理
处理多个表
[例 Ex_ODBC] 用 MFC处理多个表。
(1)选择,View”菜单?,ClassWizard”命令或按快捷键 Ctrl+W。
(2)单击 [Add Class]按钮,从下拉列表中选择,New”。
(3)在弹出的,Add Class”对话框中指定一个 CRecordset的派生类,如图。
图 9.19 定义新的 CRecordset派生类
9.2ODBC数据库管理
(4)单击 [OK]按钮,弹出,Database Options”对话框,参看图 9.7所示。
(5)选择 ODBC的数据源,My database table for VC”,单击 [OK],选择要使用的表。
(6)单击 [OK]按钮,系统自动生成 CCodeSet类所需要的代码。以后在程序中就可以通过 CCodeSet类对象来处理表 zy。
(7)将工作区窗口切换到 ResourceView页面,打开对话框资源
IDD_STU_TABLE。参看图 9.20,向对话框再添加下表的控件:
(8)打开 ClassWizard的 Member Variables标签,在 Class name中选择
CStuDlg,选中所需的控件 ID号,双击鼠标或单击 Add Variables按钮。
9.2ODBC数据库管理
(9)切换到 ClassWizard的 Messsage Maps标签页,为 CStuDlg中增加 WM_INITDIALOG
的消息映射,添加代码:
BOOL CStuDlg::OnInitDialog()
{ CDialog::OnInitDialog();
CCodeSet codeSet;
codeSet.Open();
while (!codeSet.IsEOF())
{ m_CodeList.AddString(codeSet.m_profcode);
codeSet.MoveNext();
}
codeSet.Close();
// 在列表框中查找由 m_ProfCode指定的专业代码,若找到则设置当前列表项
// 并调用 OnSelchangeList1函数
if (!m_ProfCode.IsEmpty())
{ int index=m_CodeList.FindString(-1,m_ProfCode);
if (index!=LB_ERR){
m_CodeList.SetCurSel(index);
OnSelchangeList1();
}
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION,OCX Property Pages should return FALSE
}
9.2ODBC数据库管理
(10)用 ClassWizard为控件 IDC_LIST1增加 LBN_SELCHANGE的消息映射,并添加下列代码:
void CStuDlg::OnSelchangeList1()
{ int index=m_CodeList.GetCurSel();
if (index!=LB_ERR)
{ CString str;
m_CodeList.GetText(index,str);
// 在记录集中查找 profcode字段的值为 str的记录
CCodeSet codeSet;
codeSet.m_strFilter.Format("profcode='%s'",str);
if (codeSet.Open())
{ m_Disp.Format("%s
%d",codeSet.m_profname,codeSet.m_stuyears);
m_ProfCode=str;
UpdateData(FALSE);
}
codeSet.Close();
}
}
9.2ODBC数据库管理
(11)在 StuDlg.cpp文件的开始处增加下列语句:
#include,CodeSet.h”
(12)编译并运行。单击 [修改记录 ]按钮时,弹出如图所示的对话框。
图 9.20,学生表”对话框
9.3数据库相关的 ActiveX控件
9.3.1使用 MSFlexGrid控件
将控件的类添加到项目中
(1)选择,Project”?“Add To Project”?“Components and Controls...”,弹出,Components and Controls Gallery”对话框,如图。
图 9.21,Components and Controls Gallery”对话框
9.3数据库相关的 ActiveX控件
(2)双击,Registered ActiveX Controls”项,列出在 Windows 98系统中安装的 ActiveX控件。
(3)在列表中找到 Micorsoft FlexGrid Control,在该控件双击鼠标。如图,
向用户询问是否将此控件插入项目中。
(4)单击 [确定 ]按钮,弹出,Confirm Classes”对话框。
(5)单击 [OK]按钮接受所有的类。如图 9.23。
(6)单击 [Close]按钮关闭,Components and Controls Gallery”对话框。
图 9.22,Microsoft Visual C++”对话框 图 9.23 控件工具栏添加的控件
9.3数据库相关的 ActiveX控件
向对话框添加 MSFlexGrid控件
[例 Ex_ODBC] 在表单对话框 IDD_EX_ODBC_FORM中添加一个 MSFlexGrid控件。
首先用 ClassWizard为刚才添加的 MSFlexGrid控件增加一个 CMSFlexGrid类成员变量
m_MSFGrid。在 CEx_ODBCView类的 OnInitialUpdate函数体中添加代码:
void CEx_ODBCView::OnInitialUpdate()
{,..
m_pSet->MoveFirst();
m_MSFGrid.SetCols(m_pSet->m_nFields+1 );// 设置网格的最大列数
m_MSFGrid.SetRows(m_pSet->GetRecordCount()+1);
m_MSFGrid.SetColWidth(-1,1440);
// 将所有的网格都设为相同的列宽。 -1表示所有的列,列宽单位为一个点的
// 1/20(一个点是 1/72英寸 ),也就是说,1440刚好为 1英寸。
// 定义网格的表头
m_MSFGrid.SetRow(0);
m_MSFGrid.SetCol(1); // 定位到 (0,1)网格
m_MSFGrid.SetText("学号 "); // 设置其显示内容
m_MSFGrid.SetRow(0); m_MSFGrid.SetCol(2);
m_MSFGrid.SetText("姓名 ");
m_MSFGrid.SetRow(0); m_MSFGrid.SetCol(3);
m_MSFGrid.SetText("性别 ");
m_MSFGrid.SetRow(0); m_MSFGrid.SetCol(4);
m_MSFGrid.SetText("专业代号 ");
int iRow=1;
9.3数据库相关的 ActiveX控件
while (!m_pSet->IsEOF()) //将表的记录内容显示在网格中
{ CString str;
str.Format("记录 %d",iRow);
m_MSFGrid.SetRow(iRow);m_MSFGrid.SetCol(0);
m_MSFGrid.SetText(str);
m_MSFGrid.SetRow(iRow);m_MSFGrid.SetCol(1);
m_MSFGrid.SetText(m_pSet->m_stuid);
m_MSFGrid.SetRow(iRow);m_MSFGrid.SetCol(2);
m_MSFGrid.SetText(m_pSet->m_name);
m_MSFGrid.SetRow(iRow);m_MSFGrid.SetCol(3);
m_MSFGrid.SetText(m_pSet->m_sex);
m_MSFGrid.SetRow(iRow);m_MSFGrid.SetCol(4);
m_MSFGrid.SetText(m_pSet->m_profcode);
iRow++;
m_pSet->MoveNext();
}
m_MSFGrid.SetRow(1); m_MSFGrid.SetCol(1);
m_pSet->MoveFirst();
}
9.3数据库相关的 ActiveX控件上述代码的目的是将 m_pSet所关联的表的记录内容显示在 MSFlexGrid控件上,如图。
图 9.25 MSFlexGrid控件的结果
9.3数据库相关的 ActiveX控件
9.3.2RemoteData和 DBGrid控件
RemoteData控件
RemoteData 控件在远程数据对象 (RDO)和数据识别的被绑定的控件之间提供了接口。通过 RemoteData 控件,能够:
建立起与基于其本身属性的数据源的连接。
创建 RDO的结果集。
把当前行的数据传送给相应被绑定的控件。
允许对当前行指针进行定位。
将对被绑定的控件所做的任何更改反传给数据源。
使用时还需要注意的是:
(1)由于 RemoteData控件一般不需要程序再控制,因此向一个对话框中添加此控件时只需要在对话框中右击鼠标,选择,Insert Active Control”命令,
从,Insert Active Control”对话框的控件列表中选择 RemoteData控件,单击
[OK]。
9.3数据库相关的 ActiveX控件
(2)在 RemoteData控件属性对话框 (图 9.26)中,一般需要进行下面几方面的设置:
在,Control(控制 )”页面中,从,DataSource”的下拉列表中选择所需要的数据源名。例如前面的用户数据源名,My database for VC”。
在,SQL”编辑框中键入 SQL操作语句,例如,SELECT * FROM xs”是检索学生表 xs的所有记录。
由于该控件在这里是用于 ODBC的联接,因此该控件必须使用 ODBC的游标驱动程序 (CursorDriver )才能使用联接成功。其方法是在 RemoteData控件属性对话框的,All”页面的,CursorDriver”设置为,1-ODBC cursor”。
图 9.26 RemoteData控件的属性对话框
9.3数据库相关的 ActiveX控件
设置 DBGrid控件
在 DBGrid控件的属性对话框中,将数据源 (DataRource)设置为 RemoteData
控件 IDC_REMOTEDATACTL1就可以了,如图。
在 Ex_ODBC的表单对话框 IDD_EX_ODBC_FORM中添加 RemoteData控件和一个 DBGrid控件,并按上述的过程进行操作,如图。
图 9.27 DBGrid控件的属性对话框 图 9.28 DBGrid控件的结果