1、第12章数据库编程,现行的数据库模型主要有4种:层次模型、网状模型、关系模型和面向对象模型。现在最流行的数据库软件都是关系模型,最有希望的模型就是面向对象模型。现有的数据库软件有很多,如大型数据库Oracle、SQL Server,小型数据库Access等,都支持关系模型,至于数据库系统的选择完全根据用户的需求。 Visual C+从4. 0版本开始就引进了对数据库的支持,而且在随后的版本中逐步丰富了多种方法,如 ODBC、ADO和DAO等,本章将针对ODBC在数据库中的编程进行介绍。,12.1ODBC简介,微软推出了开放数据库互连技术Open Database Connectivity,简称
2、ODBC。它包含访问不同数据库所要求的ODBC驱动程序。ODBC提供了应用程序接口(API),使得任何一个数据库都可以通过ODBC驱动器与指定的DBMS相联。用户的程序可通过调用ODBC驱动管理器中相应的驱动程序达到管理数据库的目的。一个基于ODBC的应用程序对数据库的操作不依赖任何DBMS,不直接与DBMS打交道,所有的数据库操作由对应的DBMS的ODBC驱动程序完成。也就是说,不论是Oracle、SQL Server还是Access数据库,都可以使用ODBC API进行访问。由此可见,ODBC的最大优点是能以统一的方式处理所有的数据库。,12.1.1ODBC的基本构成,ODBC是Micro
3、soft的Windows系统下的数据库服务的一部分,ODBC的基本构成如图12-1所示。,1.ODBC API包含在一个动态库中的函数集合、一个错误代码的集合、一个标准的SQL语句集合,用来调用DBMS中的数据。2.ODBC管理器这是一个ODBC控制台,用来管理不同的数据源。应用程序要访问数据库,首先必须在ODBC管理器中创建一个数据源。ODBC管理器根据数据源提供的数据库存储位置、类型及ODBC驱动程序信息,建立起ODBC与一个特定数据库之间的联系,以后程序中只需要提供数据源名称,ODBC就可以直接连接相关的数据库。ODBC管理器在系统控制面板中。,3.驱动程序管理器驱动程序管理器位于动态库
4、文件ODBC32.DLL,应用程序通过驱动程序管理器调用特定的数据库的驱动程序,驱动程序在执行完相应操作后,再将结果通过驱动程序管理器返回。驱动程序管理器支持一个应用程序同时访问多个DBMS中的数据。4.ODBC驱动程序由一个或是多个DLL构成,提供ODBC与数据库之间的接口。DLL是一个作为共享函数库的可执行文件,它使进程可以调用不属于本身可执行代码中的函数。函数的可执行代码位于一个独立的动态链接库文件中,这样可以节省内存和磁盘的存储空间,同时使程序更易于升级。DLL是一个包含可由多个程序同时使用的代码和数据的库。,12.1.2配置ODBC数据源配置ODBC数据源的步骤如下:(1)在Wind
5、ows控制面板中双击“管理工具” 图标,在弹出的窗口中能找到“数据源ODBC”图标,并双击,弹出数据源管理器对话框,如图12-2所示。,(2)在ODBC数据源管理器对话框中选系统DSN或用户DSN,用户DSN表示用户数据源只对当前用户可见,而且只能用于当前机器上。系统DSN表示系统数据源对当前机器上的所有用户可见。击添加,出现创建新数据源对话框,如图12-3所示。,(3)在创建新数据源对话框中,选择Microsoft Access Driver(*.mdb),击完成。此时弹出数据库安装对话框,如图12-4所示。输入数据源名My_Access,击选择按钮后弹出选择数据库对话框,如图12-5所示。
6、,图12-5选择数据库对话框(4)在选择数据库对话框,选择已创建好的数据库My_Access_db.mdb,击确定。回到图12-2 的ODBC数据源管理器对话框,可以看见已经添加了新数据源。,12.1.3MFC提供的有关ODBC的常用类 1.CRecordView类 一个CRecordView对象就是用一个视图中的控件来显示数据库中的记录。CRecordView类使用了动态数据交换(DDX)和数据库交换(RFX),在视图上的控件和数据源中的数据库之间进行数据交换。一个CRecordView类对象包括菜单、工具条及对话框(称为FORM表单),负责记录集的用户界面。控件连接的变量要加到CRecor
7、dView类的子类 CODBCView中,菜单消息的消息映射要放到CODBCView中。,2.CRecordset类 CRecordset类表示从数据源读取出来的数据库。创建数据库应用程序框架后,系统已经生成了CRecordView的子类CODBCView和 CRecordset类的子类CODBCSet,并和相应的数据源关联,还生成了一个对话框资源IDD_ODBC_FORM。还定义了一个指向记录集的指针m_pSet,可以通过该指针访问记录集类的数据成员以及调用记录集类的成员函数。表12-1是记录集类的数据成员,表12-2是记录集类的成员函数。,表12-1记录集类的数据成员,表12-2 记录集类
8、的成员函数,例如要实现定位功能,可以使用如下语句:m_pSet-MoveFirst();m_pSet-MoveNext();m_pSet-MoveLast();要实现当前记录的编辑、删除、更新或添加记录功能,可以使用如下语句:m_pSet-Edit(); m_pSet-Delete();m_pSet-Update();m_pSet-AddNew(); /添加一个记录m_pSet-SetFieldNull(NULL); /清空屏幕m_pSet-GetRecordCount(); /获得当前记录数,要判断当前更新、滚动状态,可以使用如下语句:m_pSet-CanUpdate()m_pSet-Can
9、Scroll()m_pSet-IsEOF()m_pSet-IsBOF()m_pSet-IsDeleted()要判断当前表是否为空,可以使用如下语句:CRecordsetStatus m_cStatus;m_pSet-GetStatus(m_cStatus);if(m_cStatus.m_lCurrentRecord=0),要实现排序功能,可以使用如下语句:m_pSet-Close();/关闭数据库m_pSet-m_strSort=“年龄”;/指定排序字段m_pSet-Open();/再次打开数据库UpdateData(FALSE);/更新已经排序过的记录或m_pSet-m_strSort=“年
10、龄”; / /指定排序字段m_pSet-Requery();/重新查询数据源UpdateData(FALSE);要实现过滤功能,可以使用如下语句:m_pSet-Close();/关闭原来的表单m_pSet-m_strFilter=“姓名=张三 ”;/将查询条件给过滤器m_pSet-Open();/打开经过过滤的表单,3.CDatabase类 CDatabase类在afxdb.h中定义。其对象是用来连接一个数据源的。为了可以使用 CDatabase 对象,需要调用构造函数,并调用OpenEx()或是Open()函数来打开一个连接。当构造一个CDatabase类对象后,可以向该对象传递这个CDat
11、abase类的指针。连接数据源结束时,必须用Close()函数关闭这个数据库对象。,4.CDBException类CDBException类是用来处理从其它ODBC类传过来的异常情况的。这个类一般是和关键字CATCH连用的。CDBException类的数据成员有如下几个: m_nRetCode:它包含了一个结构体RETCODE,里面包含了ODBC的错误信息的描述。 m_strError:包含一个描叙异常情况的字符串。m_strStateNativeOrigin:包含描述异常情况的字符串m_strStateNativeOrigin;如果变量包含多个错误的描述,错误会分行显示。,12.2简单的MF
12、C ODBC数据库应用编程用MFC应用程序向导使用ODBC数据库的一般过程是:(1)用Access或其它数据库工具构造一个数据库;(2)在Windows中为刚才构造的数据库定义一个ODBC数据源;(3)在创建数据库处理的文档应用程序向导中选择数据源;(4)设计界面,并使控件与数据表字段关联。,【例12-1】编写一个数据库应用程序,用它可以浏览数据库表中的记录。程序运行时的界面如图12-6所示。,实现步骤如下:(1)创建一个名为Student_Access的Access数据库,数据库中包含一个student表。表结构见表12-3。将其注册为ODBC数据源Student_Access。 表12-3
13、student表结构,(2)用MFC应用程序向导来生成一个单文档的应用程序MyODBC。 (3)在MFC应用程序向导的步骤2中选择数据支持的时候选择“查看数据库不使用文件支持”,如图12-7所示。表12-4中是这4个选项的说明。图12-7MFC应用程序向导的步骤2,表12-4 4个选项的说明,(4)然后选择数据源,按下数据源按钮,弹出如图12-8所示的Datasouce Options对话框,选择已经注册好的数据源Student_Access。在Recordset type中,Snapshots是一个静态数据库,每一个表都是一个从打开的数据源读取出来的。而当用户在一个Dynasets中翻阅记录
14、时,会随时显示其它人或自己对某个数据的修改,不论对这个数据的修改是在应用程序中还是其它地方。这里选Dynasets,击OK按钮。,(5)出现Select Database Tables对话框。在弹出的对话框选择student表,击OK。(6)在MFC应用程序向导的步骤2中看到选择的表名,击完成。(7)打开资源管理器的Dialog文件夹,选择IDD_ODBC_FORM,在对话框中按图12-6所示添加静态文本控件和编辑框控件,设置各控件的属性。(8)在表单视图CODBCView中添加的控件要与表的字段相关联,这样就可以根据表的当前记录位置显示相应的数据。右击控件,打开类向导,选择Member Va
15、riables标签页,类名选择CODBCView(添加CODBCView类的成员变量),击Add Variable按钮,弹出Add Member Variable对话框,如图12-9所示,选择要连接的表的字段即可。,图12-9 Add Member Variable对话框(9)编译运行程序。,需要说明的是,MFC应用程序向导创建的ODBC应用程序与一般默认的单文档应用程序相比较,在类框架方面有如下几点不同:(1)添加了一个CODBCSet类,它与上述过程中所选择的数据表student进行数据绑定,也就是说,CODBCSet对象的操作实质上对数据表进行操作。(2)将CODBCView类的基类设置
16、成CrecordView。由于CrecordView的基类是CformView,因此它需要与之相关联的表单资源。(3)MFC为用户自动创建了用于浏览数据表记录的工具按钮和相应的“记录”菜单项。若用户选择这些浏览记录命令,系统会自动调用相应的函数来移动数据表的当前位置。,(4)RFXRFX (Record Field Exchange)是支持应用程序的一个交换机制,当CODBCSet 类与 Cdatabase类在交换数据的时候没有选择大容量交换的方式(Bulk RFX)时,RFX机制将在数据交换中起作用。RFX 在视图和数据源之间自动交换数据,由于一次交换的数据可能不止一个,为此可能要多次调用D
17、oFieldExchange 函数,同时它也是应用程序框架和ODBC交流的媒介。 RFX机制能够安全的通过函数调用来保存用户的工作。,下面代码就是例12-1工程文件中应用程序向导自动加入的RFX代码,见粗斜体部分:void CMyODBCSet:DoFieldExchange(CfieldExchange* pFX)/AFX_FIELD_MAP(CMyODBCSet)pFX-SetFieldType(CfieldExchange:outputColumn);RFX_Text(pFX, _T(“Number”), m_Number);RFX_Text(pFX, _T(“Name”), m_Nam
18、e);RFX_Text(pFX, _T(“Sex”), m_Sex);RFX_Long(pFX, _T(“Age”), m_Age);RFX_Text(pFX, _T(“Special”), m_Special);/AFX_FIELD_MAP,下面是Crecordset派生类的头文件,其中关于RFX机制的部分已经用粗斜体显示:class CMyODBCSet : public Crecordsetpublic:CMyODBCSet(Cdatabase* pDatabase = NULL);DECLARE_DYNAMIC(CMyODBCSet)/ Field/Param Data/AFX_FIE
19、LD(CMyODBCSet, Crecordset)Cstringm_Number;Cstringm_Name;Cstringm_Sex;longm_Age;Cstringm_Special;/AFX_FIELD,/ Overrides/ ClassWizard generated virtual function overrides/AFX_VIRTUAL(CMyODBCSet)public:virtual Cstring GetDefaultConnect();/ Default connection stringvirtual Cstring GetDefaultSQL(); / def
20、ault SQL for Recordsetvirtual void DoFieldExchange(CfieldExchange* pFX);/ RFX support/AFX_VIRTUAL/ Implementation#ifdef _DEBUGvirtual void AssertValid() const;virtual void Dump(CdumpContext,【例12-2】在例12-1基础上增加查询功能,程序运行界面如图12-10所示。图12-10 例12-2程序运行界面,具体步骤如下:(1)添加一个组合框和一个编辑框控件,用于设置查询条件,添加一个查询按钮。控件相关的属性及
21、连接的变量如表12-3。 表12-3控件属性及连接的变量,(2)在初始化函数voidCMyODBCView:OnInitialUpdate()中,加入组合框的选择内容,代码如下:void CMyODBCView:OnInitialUpdate()m_pSet = ,(3)为查询按钮添加消息处理函数OnButtonQuery(),添加代码:void CMyODBCView:OnButtonQuery()/ TODO: Add your control notification handler code hereUpdateData();if(m_strField.IsEmpty()在视图中显示查
22、询的结果集。,【例12-3】在例12-2的基础上增加添加、修改、删除一个记录的功能,程序运行界面如图12-11所示。当单击“添加”“修改”按钮时弹出一个对话框来显示要添加或修改的内容,如图12-12所示。单击“删除”则直接删除当前记录。图12-11 例12-3程序运行界面,图12-12 弹出的对话框具体步骤如下:(1)在界面上添加三个命令按钮“添加记录”、“修改记录”和“删除记录”,设置它们的ID号分别为IDC_BUTTON_ADD、IDC_BUTTON_EDIT和IDC_BUTTON_DEL。,(2)为程序添加一个对话框资源,按照如图12-12布局。(3)双击对话框模板或按Ctr+W快捷键,
23、为对话框资源创建一个对话框类CstudentDlg。(4)为新对话框类的各个控件连接变量,见表12-4。表12-4控件属性及连接的变量,(5)为“添加记录”按钮添加消息处理函数并添加代码如下:void CMyODBCView:OnBttonAdd()/ TODO: Add your control notification handler code hereCstudentDlg dlg;if(dlg.DoModal()=IDOK)m_pSet-AddNew();m_pSet-m_Number=dlg.m_dlgNumber;m_pSet-m_Name=dlg.m_dlgName;m_pSet
24、-m_Age=dlg.m_dlgAge;m_pSet-m_Special=dlg.m_dlgSpecial;m_pSet-m_Sex=dlg.m_dlgSex;m_pSet-Update();m_pSet-Requery();,(6)为“修改记录”按钮添加消息处理函数并添加代码如下:void CMyODBCView:OnButtonEdit()/ TODO: Add your control notification handler code hereCstudentDlg dlg;dlg.m_dlgNumber=m_pSet-m_Number;dlg.m_dlgName=m_pSet-m_N
25、ame;dlg.m_dlgSex=m_pSet-m_Sex;dlg.m_dlgAge=m_pSet-m_Age;dlg.m_dlgSpecial=m_pSet-m_Special;UpdateData();,if(dlg.DoModal()=IDOK)m_pSet-Edit();m_pSet-m_Number=dlg.m_dlgNumber;m_pSet-m_Name=dlg.m_dlgName;m_pSet-m_Sex=dlg.m_dlgSex;m_pSet-m_Age=dlg.m_dlgAge;m_pSet-m_Special=dlg.m_dlgSpecial;m_pSet-Update(
26、);UpdateData(FALSE);,(7)为“删除记录”按钮添加消息处理函数并添加代码如下:void CMyODBCView:OnButtonDel()/ TODO: Add your control notification handler code hereCrecordsetStatus status;m_pSet-GetStatus(status);m_pSet-Delete();if(status.m_lCurrentRecord=0)m_pSet-MoveNext();elseUpdateData(FALSE);,(8)为新对话框的“确定”按钮添加消息处理函数并添加代码如下:
27、void CstudentDlg:OnOK()/ TODO: Add extra validation hereUpdateData();if(m_dlgNumber.IsEmpty()MessageBox(“学号不能为空”);elseCdialog:OnOK();(9)由于在视图中响应了对话框的操作,因此还需要再在CMyODBCView.cpp中加入对话框类的头文件,代码如下:#include ”CstudentDlg.h”(10)编译运行。,【例12-4】在例12-3基础上增加排序功能,程序运行界面如图12-13所示。选择排序的条件后按下“排序”按钮,可以按指定的条件排列顺序。图12-13
28、 例12-4程序运行界面,具体步骤如下:(1)在界面上添加一个组合框和一个“排序”按钮,它们的属性及连接的变量见表12-5。 表12-5控件属性及连接的变量,(2)在初始化函数void CMyODBCView:OnInitialUpdate()中,加入组合框的选择内容,代码如下:void CMyODBCView:OnInitialUpdate()m_Sort.AddString(“Number”);m_Sort.AddString(“Name”);m_Sort.AddString(“Sexr”);m_Sort.AddString(“Age”);m_Sort.AddString(“Special
29、”);,(3)为“排序” 按钮添加消息处理函数并添加代码如下:void CMyODBCView:OnButtonSort()/ TODO: Add your control notification handler code hereUpdateData();m_pSet-Close();m_pSet-m_strSort=m_Sort;m_pSet-Open();UpdateData(FALSE);,上述代码中,先通过Close()函数关闭数据库,然后设置变量m_strSort,实现按指定字段进行从小到大的顺序排序,然后再通过Open()函数再次打开数据库,最后调用UpdateData(FAL
30、SE);在视图中显示的排序结果。(4)编译运行。,【例12-5】在例12-4基础上增加定位功能,程序运行界面如图12-14所示。单击“移动到记录”按钮,弹出移动到记录对话框如图12-15所示,在对话框中可以指定记录序号。图12-14 例12-5程序运行界面,图12-15移动到记录对话框具体步骤如下:(1)添加一个“移动到记录”按钮,ID为IDC_BUTTON_MOVETO。(2)为程序添加一个对话框资源,如图12-15。,(3)为对话框资源创建一个对话框类CmoveToDlg。(4)为对话框的编辑框连接一个long类型变量m_Record。(5)为“移动到记录”按钮添加消息处理函数OnButtonMoveTo(),添加代码如下:void CMyODBCView:OnButtonMoveTo()/ TODO: Add your control notification handler code hereCmoveToDlg dlg;if(dlg.DoModal()=IDOK)m_pSet-SetAbsolutePosition(dlg.m_Record);UpdateData(FALSE);(6)在CMyODBCView.cpp中加入CmoveToDlg头文件#include “CmoveToDlg.h”(7)编译运行。,