第6章MFC ODBC编程.docx
《第6章MFC ODBC编程.docx》由会员分享,可在线阅读,更多相关《第6章MFC ODBC编程.docx(53页珍藏版)》请在冰豆网上搜索。
![第6章MFC ODBC编程.docx](https://file1.bdocx.com/fileroot1/2022-12/7/ca0a464a-7ce3-474b-bce3-e9505c95e299/ca0a464a-7ce3-474b-bce3-e9505c95e2991.gif)
第6章MFCODBC编程
第6章MFCODBC编程
为了简化开发人员编写数据库应用程序,VC++的在其基础类库(MFC)里对ODBCAPI进行了封装,实现了一个面向对象的数据库编程接口,使VC++的数据库变得更加容易。
本章首先对MFCODBC的概貌进行简要介绍,然后讲述利用MFCODBC进行数据库开发的技巧,最后将通过具体数据库开发实例,详细讲述通过MFCODBC开发数据库应用程序的方法和过程。
6.1了解MFCODBC
MFC是MicrosoftFoundationClass(微软基础类库)的缩写,它的设计目标是简化开发人员的工作。
MFC使开发人员创建基于Windows的应用程序,而不必掌握下层的Windows体系结构。
由于数据库应用程序是管理数据的重要方面,Microsoft开发了ODBCAPI的封装程序,为ODBC编程提供了一个面向对象的方法。
MFC对ODBC的封装主要是开发了CDatabase类和CRecordSet类。
6.1.1CDatabase类
CDatabase类用于应用程序建立同数据源的连接。
CDatabase类包含一个m_hdbc变量,它代表了数据源的连接句柄。
如果要建立CDatabase类的实例,应先调用该类的构造函数,再调用Open函数,通过调用,初始化环境变量,并执行与数据源的连接。
关闭数据源连接的函数是Close。
CDatabase类提供了对数据库进行操作的函数,为了执行事务操作,CDatabase类提供了BeginTrans函数,当全部数据都处理完成后,可以通过调用CommitTrans函数提交事务,或者在特殊情况下通过调用Rollback函数将处理回退。
CDatabase类提供的函数可以用于返回数据源的特定信息,例如通过GetConnect函数返回在使用函数Open连接数据源时的连接字符串,通过调用IsOpen函数返回当前的CDatabase实例是否已经连接到数据源上,通过调用CanUpdate函数返回当前的CDatabase实例是否是可更新的,通过调用CanTransact函数返回当前的CDatabase实例是否支持事务操作,等等。
总之,CDatabase类为C++数据库开发人员提供了ODBC的面向对象的编程接口。
6.1.2CRecordSet类
要实现对结果集的数据操作,就要用到CRecordSet类。
CRecordSet类定义了从数据库接收或者发送数据到数据库的成员变量,CRecordSet类定义的记录集可以是表的所有列,也可以是其中的一列,这是由SQL语句决定的。
CRecordSet类的成员变量m_hstmt代表了定义该记录集的SQL语句句柄,m_nFields成员变量保存了记录集中字段的个数,m_nParams成员变量保存了记录集所使用的参数个数。
CRecordSet的记录集通过CDatabase实例的指针实现同数据源的连接,即CRecordSet的成员变量m_pDatabase。
如果记录集使用了WHERE子句,m_strFilter成员变量将保存记录集的WHERE子句的内容,如果记录集使用了ORDERBY子句,m_strSort成员变量将保存记录集的ORDERBY子句的内容。
由多种方法可以打开记录集,最常用的方法是使用Open函数执行一个SQLSELECT语句。
有如下四种类型的记录集:
∙CRecordset:
:
dynaset:
动态记录集,支持双向游标,并保持同所连接的数据源同步,对数据的更新操作可以通过一个fetch操作获取。
∙CRecordset:
:
snapshot:
静态快照,一旦形成记录集,此后数据源的所有改变都不能体现在记录集里,应用程序必须重新进行查询,才能获取对数据的更新。
该类型记录集也支持双向游标。
∙CRecordset:
:
dynamic:
同CRecordset:
:
dynaset记录集相比,CRecordset:
:
dynamic记录还能在fetch操作里同步其它用户对数据的重新排序。
∙CRecordset:
:
forwardOnly:
除了不支持逆向游标外,其它特征同CRecordset:
:
snapshot相同。
6.2MFCODBC数据库访问技术
6.2.1记录查询
使用CRecordSet的Open()和Requery()成员函数可以实现记录查询。
需要注意的是,在使用CRecordSet的类对象之前,必须使用CRecordSet的成员函数Open()来获得有效的记录集。
一旦使用过Open()函数,再次查询时使用Requery()函数就可以了。
在调用Open()函数时,如果已经将一个打开的CDatabase对象指针传递给CRecordSet类对象的m_pDatabase成员变量,那么,CRecordSet类对象将使用该数据库对象建立ODBC连接;否则,如果m_pDatabase为空指针,对象就需要就新建一个CDatabase类对象并使其与缺省的数据源相连,然后进行CRecordSet类对象的初始化。
缺省数据源由GetDefaultConnect()函数获得。
也可以通过特定的SQL语句为CRecordSet类对象指定数据源,并以它来调用CRecordSet类的Open()函数,例如:
myRS.Open(AFX_DATABASE_USE_DEFAULT,strSQL);
如果没有指定参数,程序则使用缺省的SQL语句,即对在GetDefaultSQL()函数中指定的SQL语句进行操作,代码如下:
CStringCMyRS:
:
GetDefaultSQL()
{return_T("[Name],[Age]");}
对于GetDefaultSQL()函数返回的表名,对应的缺省操作是SELECT语句,例如:
SELECT*FROMBasicData,MainSize
在查询过程中,也可以利用CRecordSet类的成员变量m_strFilter和m_strSort来执行条件查询和结果排序。
m_strFilter用于指定过滤字符串,存放着SQL语句中关键字WHERE后的条件语句;m_strSort用于指定用于排序的字符串,存放着SQL语句中关键字ORDERBY后的字符串。
例如:
myRS.m_strFilter="Name='刘鹏'";
myRS.m_strSort="Age";
myRS.Requery();
数据库查询中对应的SQL语句为:
SELECT*FROMBasicData,MainSizeWHEREName='刘鹏'ORDERBYAge
除了直接赋值给成员变量m_strFilter以外,还可以通过参数化实现条件查询。
利用参化可以更直观、更方便地完成条件查询任务。
参数化方法的步骤如下:
(1)声明参变量,代码如下:
CStringstrName;
intnAge;
(2)在构造函数中初始化参变量如下:
strName=_T("");
nAge=0;
m_nParams=2;
(3)将参变量与对应列绑定,代码如下:
pFX->SetFieldType(CFieldExchange:
:
param)
RFX_Text(pFX,_T("Name"),strName);
RFX_Single(pFX,_T("Age"),nAge);
完成以上步骤之后就可以利用参变量进行条件查询了,代码如下:
m_pmyRS->m_strFilter="Name=?
ANDage=?
";
m_pmyRS->strName="刘鹏";
m_pmyRS->nAge=26;
m_pmyRS->Requery();
参变量的值按绑定的顺序替换查询字串中的“?
”通配符。
如果查询的结果是多条记录,可以利用CRecordSet类的成员函数Move(),MoveNext(),MovePrev(),MoveFirst()和MoveLast()来移动记录光标。
6.2.2记录添加
使用AddNew()成员函数能够实现记录添加,需要注意的是,在记录添加之前必须保证数据库是以允许添加的方式打开的,代码如下:
m_pmyRS->AddNew();//在表的末尾添加新记录
m_pmyRS->SetFieldNull(&(m_pSet->m_type),FALSE);
m_pmyRS->m_strName="刘鹏";//输入新的字段值
m_pmyRS->m_nAge=26;//输入新的字段值
m_pmyRS->Update();//将新记录存入数据库
m_pmyRS->Requery();//重新建立记录集
6.2.3记录删除
调用Delete()成员函数能够实现记录删除,在调用Delete()函数后不需调用Update()函数,代码如下:
m_pmyRS->Delete();
if(!
m_pmyRS->IsEOF())
m_pmyRS->MoveNext();
else
m_pmyRS->MoveLast();
6.2.4记录修改
调用Edit()成员函数可以实现记录修改,在修改完成后需要调用Update()将修改结果存入数据库,代码如下:
m_pmyRS->Edit();//修改当前记录
m_pmyRS->m_strName="刘波";//修改当前记录字段值
...
m_pmyRS->Update();//将修改结果存入数据库
m_pmyRS->Requery();
6.2.5撤销数据库更新操作
如果用户增加或者修改记录后希望放弃当前操作,可以在调用Update()函数之前调用Move()函数,就可以使数据库更新撤销了,代码如下:
CRecordSet:
:
Move(AFX_MOVE_REFRESH);
该函数用于撤消增加或修改模式,并恢复在增加或修改模式之前的当前记录。
其中参数AFX_MOVE_REFRESH的值为零。
6.2.6直接执行SQL语句
虽然通过CRecordSet类我们可以完成大多数的数据库查询操作,而且在CRecordSet类的Open()成员函数中也可以提供SQL语句,但有的时候我们还想进行一些其他操作,例如建立新表、删除表、建立新的字段等等,这时就需要用到CDatabase类的直接执行SQL语句的机制。
通过调用CDatabase类的ExecuteSQL()成员函数就能够完成QL语句的直接执行,代码如下:
BOOLCMyDB:
:
ExecuteSQLWithReport(constCString&strSQL)
{
TRY
{
m_pMyDB->ExecuteSQL(strSQL);//直接执行SQL语句
}
CATCH(CDBException,e)
{
CStringstrMsg;
strMsg.LoadString(IDS_EXECUTE_SQL_FAILED);
strMsg+=strSQL;
returnFALSE;
}
END_CATCH
returnTRUE;
}
需要注意的是,由于不同DBMS提供的数据操作语句不尽相同,直接执行SQL语句可能会破坏软件的DBMS无关性,因此在应用中应当慎用此类操作。
6.2.7MFCODBC的数据库操作过程
同ODBCAPI编程类似,MFC的ODBC编程也要先建立同ODBC数据源的连接,这个过程由一个CDatabase对象的Open函数实现。
然后CDatabase对象的指针将被传递到CRecordSet对象的构造函数里,使CRecordSet对象与当前建立起来的数据源连接结合起来。
完成数据源连接之后,大量的数据库编程操作将集中在记录集的操作上。
CRecordSet类的丰富的成员函数可以让开发人员轻松地完成基本的数据库应用程序开发任务。
当然,完成了所有的操作之后,在应用程序退出运行状态的时候,需要将所有的记录集关闭,并关闭所有同数据源的连接。
6.3MFCODBC编程实例
6.3.1实例概述
需求调查与分析
某贸易公司经理需要对公司日常处理的业务通过计算机进行监控,因此需要开发一个浏览数据和报表的数据库应用软件,该软件的主要功能是数据表示和报表。
数据的内容包括公司日常的销售报告、产品情况、客户情况、雇员情况以及与公司合作的运货商的情况,数据报表的主要目的是能够将浏览的信息以报表的方式打印出来。
数据库系统及其访问技术
在本实例里,我们采用MFC的ODBC数据库访问技术,从Access数据库里读取公司销售信息、产品情况、客户信息、雇员信息以及与公司合作的运货商的信息。
这是一个小型的数据库应用,使用Access数据库就足够了。
在这个实例里,我们借助MFC对ODBC封装类CRecordset,从该类派生应用程序里使用的CCommonRs类。
以CCommonRs类为基础,实现对ODBC数据源的数据访问。
实例实现效果
ODBCDemo2是本书用于阐述MFCODBC数据库编程的实例应用程序,该应用程序实现了对某销售公司日常销售业务的信息浏览和报表操作。
应用程序运行界面如图6-1所示。
图6-1ODBCDemo2实例应用程序的运行界面
6.3.2实例实现过程
数据库设计
我们利用MicrosoftAccess工具设计本实例的数据库结构。
在本实例里,我们需要利用数据库存放销售公司的如下信息:
∙日常销售信息:
指公司日常销售帐单中的客户信息、雇员信息、订购日期、货主信息、运货商信息。
∙产品信息:
指公司现经营产品的产品名称、供应商信息、类别信息、单价以及库存量等信息。
∙客户信息:
指公司客户的客户名称、联系人信息和客户的地址信息。
∙雇员信息:
指公司雇员的雇员姓名、头衔、尊称、出生日期、雇佣日期、联系信息及其上级等信息。
∙供应商信息:
指与公司合作的供应商的名称、地址、联系人等信息。
∙运货商信息:
指与公司合作的运货商的名称、电话信息。
∙产品类别信息:
指公司所经营产品的类别。
我们为数据库设计了八个表,表“订单”和表“订单明细”存放公司日常销售信息,表“产品”存放公司的产品信息,表“客户”存放公司的客户信息,表“雇员”存放公司的雇员信息,表“供应商”存放公司的产品供应商信息,表“运货商”存放公司的运货商信息,表“类别”存放公司经营产品的类别信息。
为了便于数据访问,我们还定义了三个视图,“SalesByCustomer”视图管理着以客户为统计方式的销售信息,“SalesByEmployee”视图管理着以雇员为统计方式的销售信息,“SalesByProduct”视图管理着以产品为统计方式的销售信息。
下面的表6-1列出了表“订单”的结构,表6-2列出了表“订单明细”的结构,表6-3列出了表“产品”的结构,表6-4列出了表“客户”的结构,表6-5列出了表“雇员”的结构,表6-6列出了表“供应商”的结构,表6-7列出了表“运货商”的结构,表6-8列出了表“类别”的结构。
表6-1表“订单”的结构
字段名称
类型
字段名称
类型
订单ID(key)
自动编号
运货费
货币
客户ID
文本
货主名称
文本
雇员ID
数字
货主地址
文本
订购日期
日期/时间
货主城市
文本
到货日期
日期/时间
货主地区
文本
发货日期
日期/时间
货主邮政编码
文本
运货商
数字
货主国家
文本
表6-2表“订单明细”的结构
字段名称
类型
字段名称
类型
订单ID
自动编号
数量
数字
产品ID
数字
折扣
数字
单价
货币
表6-3表“产品”的结构
字段名称
类型
字段名称
类型
产品ID(key)
自动编号
单价
货币
产品名称
文本
库存量
数字
供应商ID
数字
订购量
数字
类别ID
数字
再订购量
数字
单位数量
文本
中止
是/否
表6-4表“客户”的结构
字段名称
类型
字段名称
类型
客户ID(key)
自动编号
地区
文本
公司名称
文本
邮政编码
文本
联系人姓名
文本
国家
文本
联系人头衔
文本
电话
文本
地址
文本
传真
文本
城市
文本
表6-5表“雇员”的结构
字段名称
类型
字段名称
类型
名字ID
自动编号
国家
文本
头衔
文本
邮政编码
文本
尊称
文本
家庭电话
文本
出生日期
日期/时间
分机
文本
雇用日期
日期/时间
照片
OLE对象
地址
文本
备注
备注
城市
文本
上级
文本
地区
文本
表6-6表“供应商”的结构
字段名称
类型
字段名称
类型
供应商ID(key)
自动编号
地区
文本
公司名称
文本
邮政编码
文本
联系人姓名
文本
国家
文本
联系人头衔
文本
电话
文本
地址
文本
传真
文本
城市
文本
主页
超级链接
表6-7表“运货商”的结构
字段名称
类型
字段名称
类型
运货商ID(key)
自动编号
电话
货币类型
公司名称
文本
表6-8表“类别”的结构
字段名称
类型
字段名称
类型
类别ID
自动编号
说明
备注
类别名称
文本
图片
OLE对象
在实例光盘的Database目录下,stocks.mdb文件是存放公司销售信息的Access数据库文件,读者可以查看这个文件,了解详细信息。
创建ODBCDemo2工程
ODBCDemo2工程是一个基于单文档的应用程序,创建应用程序工程时需要选择基于单文档的应用程序类型。
操作步骤:
(1)打开VC++的工程创建向导。
从VC++的菜单中执行“File>New”命令,将VC++6.0工程创建向导显示出来。
如果当前的选项标签不是Project,单击Project选项标签将它选中。
在左边的列表里选择MFCAppWizard(exe)项,在ProjectName编辑区里输入工程名称“ODBCDemo2”,并在Location编辑区里调整工程路径,如图6-2所示。
图6-2工程创建向导
(2)选择应用程序的框架类型。
单击“工程创建向导”窗口的OK按钮,进入“MFCAppWizard–Step1”对话框。
首先选择应用程序的框架类型。
如图6-3所示。
在本工程里,选择“Singledocument”,保持资源的语言类型为“中文”,单击“Next>”按钮。
图6-3选择应用程序的框架类型
(3)进入“MFCAppWizard–Step2of6”对话框,设置应用程序数据库特性。
在对话框里选择None,如图6-4所示。
(4)设置应用程序对复杂文档的支持。
在“MFCAppWizard–Step2of6”对话框里,单击“Next>”按钮,进入“MFCAppWizard–Step3of6”对话框。
在对话框里选择如下两项:
∙None
∙ActiveXControls
图6-4设置应用程序数据库特性
如图6-5所示,单击“Next>”按钮。
图6-5设置应用程序对复杂文档的支持
(5)进入“MFCAppWizard–Step4of6”对话框,设置应用程序的特征信息。
如图6-6所示对话框是工程的特征信息,在本例中,ODBCDemo2工程有如下特征:
∙Dockingtoolbar
∙Initialstatusbar
∙Printingandprintpreview
∙3Dcontrols
∙Normal
(6)在“MFCAppWizard–Step4of6”对话框里单击“Next>”按钮,进入“MFCAppWizard–Step5of6”对话框,选择工程风格和MFC类库的加载方式。
在对话框里设置如下三项:
∙MFCStandard
∙Yes,Please
∙AssharedDLL
如图6-7所示,单击“Next>”按钮,进入“MFCAppWizard–Step6of6”对话框。
图6-7设置应用程序特征信息
图6-8选择工程风格和MFC类库的加载方式
(7)“MFCAppWizard–Step6of6”对话框显示工程创建中的类信息。
ODBCDemo2工程包含了四个类:
∙CODBCDemo2View类,工程视图类
∙CODBCDemo2App类,工程的应用类
∙CMainFrame类,工程主框架类
∙CODBCDemo2Doc类,工程文档类
这四个类构成了应用程序工程的主要框架。
我们为CODBCDemo2View类选择ClistView基类,如图6-9所示。
图6-9显示工程创建中的类信息
(8)在对话框“MFCAppWizard–Step6of6”里单击Finish按钮,完成工程创建。
工程创建向导将该次工程创建的信息显示在一个“NewProjectInformation”对话框里,如图6-10所示。
在对话框里单击“OK”按钮,ODBCDemo2工程创建完成。
图6-10工程创建信息
建立数据源
建立数据源的方法在6.3.2节里已经进行了介绍,这里只说明本实例在建立数据源时的不同之处。
本实例用到的数据源名称是“ODBCDemo2”,其描述为“DatasourceforODBCMFCprogramming”,创建数据源时在“配置创建的新数据源”步骤里需要同ODBCDemo1数据源有所区别。
另外,数据源所支持的数据库为stocks.mdb文件,我们已经在6.3.2节里完成了它的设计。
设计应用程序界面
在6.3.2节里,我们利用工程创建向导创建了一个基于单文档界面的工程,本节应用程序界面的设计工作主要是菜单和按钮的添加。
为了为报表设置报表的内容和格式,我们还需要为应用程序设计一个报表向导,这个向导由三个步骤组成,因此我们需要设计三个对话框以实现这三个步骤的界面。
1.设计应用程序的主菜单
需要为应用程序设计的菜单包括:
销售报告菜单、产品信息菜单、客户信息菜单、雇员信息菜单、供应商信息菜单、运货商信息菜单、类别菜单以及报表操作菜单。
这些菜单的标识、标题以及提示信息如表6-9所示。
表6-9工程的菜单资源
标识
标题
提示信息
文件
ID_OPTION
选项
应用程序设置选项
ID_APP_EXIT
退出
退出应用程序;提示保存文档\n退出
销售报告
ID_SALE_PRODUCT
按产品
按照产品显示销售报告。
\n产品销售报告
ID_SALE_CUSTOMER
按客户
按照客户显示销售报告。
\n客户销售报告
ID_SALE_EMPLOYEE
按雇员
按照雇员显示销售报告。
\n雇员销售报告
产品
ID_VIEW_PRODUCT
产品信息
显示产品信息
客户
ID