380 likes | 541 Views
第 9 章数据库编程. 9.1 数据库概述 9.2 ODBC 数据库管理 9.3 数据库相关的 ActiveX 控件. 9.1 数据库概述. 数据库和 DBMS 指以一定的组织形式存放在计算机存储介质上的相互关联的数据的集合。 为了有效地管理数据库,常常需要一些数据库管理系统 (DBMS) 提供对数据库操作的各种命令、工具及方法。 SQL SQL 语句分为两类:一是 DDL 语句,用来创建表、索引等,另一是 DML ,这些语句是用来读取数据、更新数据和执行其他类似操作的语句。 ODBC 、 DAO 和 OLE DB
E N D
第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.2 ODBC数据源管理器 图9.1 Windows 98的“控制面板” 9.2ODBC数据库管理 • 定义ODBC的数据源
图9.5 用户数据源列表 图9.3 “创建新数据源”对话框 图9.4 ODBC Access 安装对话框 9.2ODBC数据库管理 • 定义用户的DSN的过程如下。 • (1)单击[添加]按钮,如图。 • (2)在该对话框中选 “Microsoft Access Driver”。单击[完成],如图。 • (3)在对话框中,单击[选取]按钮将前面创建的数据库调入。 • (4)单击[确定],如图。
图9.6 向导的第二步对话框 图9.7 “Database Options”对话框 9.2ODBC数据库管理 • MFC Appwizard的创建 • (1)用MFC AppWizard(exe)创建一个单文档应用程序Ex_ODBC。 • (2)在向导的第二步对话框中加入数据库的支持,如图。 • (3)选中“Database view with file support”项,单击[Data Source],如图。
记录浏览按钮 图9.9 Ex_ODBC运行结果 图9.8 “Select Database Tables”对话框 9.2ODBC数据库管理 • (4)选择ODBC的数据源“My database for VC”,单击[OK],如图,从中选择要使用的表(这里选择xs)。 • (5)单击[OK],回到向导的第二步对话框。单击[Finish]。 • (6)编译并运行,如图。
图9.10 控件的设计 9.2ODBC数据库管理 • 浏览记录 • (1)将前面的单文档项目Ex_ODBC调入。 • (2)切换到项目工作区窗口的ResourceView页面,打开用于表单视图CEx_ODBCView的对话框资源IDD_EX_ODBC_FORM。 • (3)参看图9.10向对话框中添加下列控件。
图9.11 为控件添加数据成员 9.2ODBC数据库管理 • (4)选择“View”菜单“Class Wizard”或按快捷键Ctrl+W,切换到Member Variables页面,在Class name框中选择CEx_ODBCView,为上述控件添加相关联的数据成员。例如,双击IDC_PROF_CODE,在弹出的对话框中的成员变量下拉列表中选择要添加的成员变量名m_pSet->m_profcode,如图。 • (5)按照上一步骤的方法,为下表的控件依次添加相关联的数据成员。
图9.12 Ex_ODBC浏览记录 9.2ODBC数据库管理 • (6)编译并运行,如图。
图9.13 “MFC ClassWizard”对话框 9.2ODBC数据库管理 • 改变与m_pSet关联的表 • (1)将ClassWizard对话框切换到“Member Variables”页面,在“Class name”的下拉列表中选择“CEx_ODBCSet”,此时ClassWizard对话框的[Update Columns和[Bind All按钮被激活。如图。
图9.14 “Database Options”对话框 图9.15 “Select Database Tables”对话框 9.2ODBC数据库管理 • (2)单击[Update Columns],如图。 • (3)从中选择ODBC的数据源“My database for VC”,单击[OK],如图,选择要使用的表。 • (4)单击[OK]按钮,以回到ClassWizard界面。 • (5)若表中的各个字段还没有相应的数据成员,可单击[Bind All]按钮进行自动设置。
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.16 显示记录信息 9.2ODBC数据库管理 • (7)编译并运行,如图。
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.18 “学生表”对话框 图9.17 Ex_ODBC的记录编辑 9.2ODBC数据库管理 • [例Ex_ODBC] 在表单视图中增加三个按钮:[添加记录]、[修改记录]和[删除记录],如图。单击[添加记录]或[修改记录]将弹出一个如图所示的对话框,在对话框中可以进行数据的添加或修改,单击对话框的[确定]则数据有效。
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.19 定义新的CRecordset派生类 9.2ODBC数据库管理 • 处理多个表 • [例Ex_ODBC] 用MFC处理多个表。 • (1)选择“View”菜单“ClassWizard”命令或按快捷键Ctrl+W。 • (2)单击[Add Class]按钮,从下拉列表中选择“New”。 • (3)在弹出的“Add Class”对话框中指定一个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.20 “学生表”对话框 9.2ODBC数据库管理 • (11)在StuDlg.cpp文件的开始处增加下列语句: • #include “CodeSet.h” • (12)编译并运行。单击[修改记录]按钮时,弹出如图所示的对话框。
图9.21 “Components and Controls Gallery”对话框 9.3数据库相关的ActiveX控件 • 9.3.1使用MSFlexGrid控件 • 将控件的类添加到项目中 • (1)选择“Project”“Add To Project”“Components and Controls...”,弹出“Components and Controls Gallery”对话框,如图。
图9.22 “Microsoft Visual C++”对话框 添加的控件 图9.23 控件工具栏 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.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.25 MSFlexGrid控件的结果 9.3数据库相关的ActiveX控件 • 上述代码的目的是将m_pSet所关联的表的记录内容显示在MSFlexGrid控件上,如图。
9.3数据库相关的ActiveX控件 • 9.3.2RemoteData和DBGrid控件 • RemoteData控件 • RemoteData 控件在远程数据对象 (RDO)和数据识别的被绑定的控件之间提供了接口。通过 RemoteData 控件,能够: • 建立起与基于其本身属性的数据源的连接。 • 创建RDO的结果集。 • 把当前行的数据传送给相应被绑定的控件。 • 允许对当前行指针进行定位。 • 将对被绑定的控件所做的任何更改反传给数据源。 • 使用时还需要注意的是: • (1)由于RemoteData控件一般不需要程序再控制,因此向一个对话框中添加此控件时只需要在对话框中右击鼠标,选择“Insert Active Control”命令,从“Insert Active Control”对话框的控件列表中选择RemoteData控件,单击[OK]。
图9.26 RemoteData控件的属性对话框 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.27 DBGrid控件的属性对话框 图9.28 DBGrid控件的结果 9.3数据库相关的ActiveX控件 • 设置DBGrid控件 • 在DBGrid控件的属性对话框中,将数据源(DataRource)设置为RemoteData控件IDC_REMOTEDATACTL1就可以了,如图。 • 在Ex_ODBC的表单对话框IDD_EX_ODBC_FORM中添加RemoteData控件和一个DBGrid控件,并按上述的过程进行操作,如图。