MFC上基于Access数据库连接实例(ODBC模式)

时间:2021-05-10 13:11:00

下面来写一个简单的小程序,程序实现的功能是能够动态的连接一个在电脑上建好的Access数据表,并能够实现插入、删除、替换数据的功能!

下面来看一下主框架界面:

MFC上基于Access数据库连接实例(ODBC模式)

这个主界面比较简单,就是四个按钮,是用来添加消息响应函数的,主要是实现数据库连接的功能,当按下SELECT测试按钮后,进行数据库连接,并将选定的数据通过对话框显示出来,以示连接成功。当按下INSERT测试按钮后,向Access数据表中插入一行数据,插入成功后显示状态,依次类推,当按下UpdateData测试后,将数据表中的某项数据更新,并显示成功状态,当按下DELETE测试后,将数据表中的某项指定数据删除,并显示成功状态!显示如下:

MFC上基于Access数据库连接实例(ODBC模式)MFC上基于Access数据库连接实例(ODBC模式)MFC上基于Access数据库连接实例(ODBC模式)MFC上基于Access数据库连接实例(ODBC模式)MFC上基于Access数据库连接实例(ODBC模式)



下面我们先介绍下数据库连接的一些基本知识:

ODBC(Open Datebase Conectivity)即开放式数据库互联,它是微软公司开放服务结构(WOSA,Windows Open Services Architecture)中有关数据库的一个组成部分,它建立了一组规范,并提供了一组对数据库访问的标准API(应用程序编程接口)。这些API利用SQL来完成其大部分任务。ODBC本身也提供了对SQL语言的支持,用户可以直接将SQL语句送给ODBC。


MFC的ODBC类对较复杂的ODBC API进行了封装,提供了简化的调用接口。MFC的ODBC主要包括以下几个类:

  • CDatabase类:主要功能是建立与数据库的连接。
  • CRecordset类:代表从数据源选择的一组记录(记录集)。
  • CRecordView类:提供了一个表单视图与某个记录集直接相连,利用对话框数据交替机制(DDX)在记录集与表单视图的控件之间传输数据。
  • CFieldExchange类:支持记录字段数据交换(RFX),即记录集字段数据成员与相应的数据库的表的字段之间的数据交换。
  • CDBException类:代表ODBC类产生的异常。

数据源是位于一些数据库管理系统(DBMS)的数据的指定实例,包括MicrosoftSQLServer 和MicrosoftAccess和BorlanddBASExBASE为使用CDatabase,构造一个CDatabase对象并调用它的OpenEx成员函数。这打开了一个连接。在接着构造CRecordset对象以操纵连接的数据源时,向CDatabase对象传递记录集构造程序指针。完成使用连接时调用Close成员函数并销毁CDatabase对象。Close关闭以前没有关闭的任何记录集。

下面我们来看具体操作:

首先我们写一个类来包含一系列连接数据库的操作:

<span style="font-family:SimHei;">#ifndef ACCESS_H_H_H
</span><span style="font-family:Times New Roman;">#define ACCESS_H_H_H
#include <afxdb.h></span><span style="font-family:SimHei;"> //MFC ODBC数据库类的定义文件
</span><span style="font-family:Times New Roman;">class Access</span><span style="font-family:SimHei;">
{
</span><span style="font-family:Times New Roman;">public</span><span style="font-family:SimHei;">:
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>Access(void)</span><span style="font-family:SimHei;">; //类的构造函数
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>virtual ~Access(void)</span><span style="font-family:SimHei;">; //类的析构函数
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>BOOL Connect(LPCTSTR szDbPath)</span><span style="font-family:SimHei;">; //实现数据源的动态注册及实现CDatabase类对象与数据源的连接,并 将CDatabase类对象的指针传递给CRecordset对象,以便该对象在后 面创建记录集
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>void DisConnect()</span><span style="font-family:SimHei;">; //断开数据集指针及数据源对象与数据源的链接
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>long Select(LPCTSTR szSQL)</span><span style="font-family:SimHei;">; //CRecordset类的对象指针调用函数创建记录集,返回的值是指向当前的 记录
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>CString GetField(const SHORT fieldIndex)</span><span style="font-family:SimHei;">; //得到记录集中的字段值,重载函数,形参分别是索引值 和名称
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>CString GetField(LPCTSTR fieldName)</span><span style="font-family:SimHei;">;
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>BOOL Qurey(LPCTSTR szSQL)</span><span style="font-family:SimHei;">; //执行一条SQL语句
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>static Access* GetInstance()</span><span style="font-family:SimHei;">; //单例模式,每次只是用一个Access对象
</span><span style="font-family:Times New Roman;">private</span><span style="font-family:SimHei;">:
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>CDatabase m_Database</span><span style="font-family:SimHei;">; //将CDatabase类的对象作为其成员变量
</span><span style="font-family:Times New Roman;"><span style="white-space:pre"></span>CRecordset *m_pRecordSet</span><span style="font-family:SimHei;">; //将CRecordset类的对象指针作为其成员变量
};
#endif</span>

该类的源文件下的几个重要函数代码:

<span style="font-family:Times New Roman;font-size:18px;">#pragma comment(lib,"odbccp32.lib")   //指定连接要使用的库,该库中包含SQLConfigDataSource函数
#include <ODBCINST.h></span>
<span style="font-family:Times New Roman;font-size:18px;"></span><pre name="code" class="cpp"><span style="font-family:Times New Roman;font-size:18px;">//实现类的单例模式</span>
Access* Access::GetInstance() { static Access instance; return &instance;}

 
<span style="font-family:Times New Roman;font-size:18px;">
BOOL Access::Connect(LPCTSTR szDbPath)
{
<span style="white-space:pre"></span>CString csTemp;
<span style="white-space:pre"></span>csTemp.Format(_T("DSN=%s;DBQ=%s"),_T("my_db"),szDbPath);//DSN:新数据源名称;DBQ:数据源地址;

//动态注册数据源
//SQLConfigDataSource()函数可以动态的增加,修改和删除数据源!</span>
<span style="font-family:Times New Roman;font-size:18px;"><span style="white-space:pre"></span>if(SQLConfigDataSource(NULL,ODBC_ADD_DSN,_T("Microsoft Access Driver (*.mdb)"),</span>
<span style="font-family:Times New Roman;font-size:18px;"><span style="white-space:pre"></span>(LPCTSTR)csTemp.GetBuf<span style="white-space:pre"></span>fer(256)))<span style="white-space:pre"></span>{<span style="white-space:pre"></span>TRACE("/**********动态注册数据源成功!***********/\n");//调试时TRACE宏信息输出到VC IDE环境<span style="white-space:pre"></span>的输出窗口;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>else<span style="white-space:pre"></span>{<span style="white-space:pre"></span>TRACE("/**********创建DSN时出现错误!!***********/\n");<span style="white-space:pre"></span>return FALSE;<span style="white-space:pre"></span>}//创建对象与数据源的连接<span style="white-space:pre"></span>if(m_Database.IsOpen()) //如果CDatabase对象当前与数据源连接,则返回非零<span style="white-space:pre"></span>{<span style="white-space:pre"></span>m_Database.Close(); //关闭数据源连接<span style="white-space:pre"></span>}<span style="white-space:pre"></span>BOOL bOpenFlag;//assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行<span style="white-space:pre"></span>ASSERT(TRUE == (bOpenFlag = m_Database.OpenEx(_T("DSN=my_db"),CDatabase::noOdbcDialog)));//使用CRecordset对象操作数据源时,必须将CDatabase对象的指针传递给他的构造函数,//并调用成员函数OPEN()创建记录集。<span style="white-space:pre"></span>if(m_pRecordSet != NULL)<span style="white-space:pre"></span>{<span style="white-space:pre"></span>if(m_pRecordSet->IsOpen())<span style="white-space:pre"></span>{<span style="white-space:pre"></span>m_pRecordSet->Close();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>delete(m_pRecordSet);<span style="white-space:pre"></span>m_pRecordSet = NULL;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>if(m_pRecordSet == NULL)<span style="white-space:pre"></span>{<span style="white-space:pre"></span>m_pRecordSet = new CRecordset(&m_Database);<span style="white-space:pre"></span>}<span style="white-space:pre"></span>return bOpenFlag;}//关闭数据库void Access::DisConnect(){<span style="white-space:pre"></span>if(m_pRecordSet != NULL)<span style="white-space:pre"></span>{<span style="white-space:pre"></span>if(m_pRecordSet->IsOpen())<span style="white-space:pre"></span>{<span style="white-space:pre"></span>m_pRecordSet->Close();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>delete(m_pRecordSet);<span style="white-space:pre"></span>m_pRecordSet = NULL;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>if(m_Database.IsOpen())<span style="white-space:pre"></span>{<span style="white-space:pre"></span>m_Database.Close();<span style="white-space:pre"></span>}}long Access::Select(LPCTSTR szSQL){<span style="white-space:pre"></span>if(m_pRecordSet->IsOpen())<span style="white-space:pre"></span>{<span style="white-space:pre"></span>m_pRecordSet->Close();<span style="white-space:pre"></span>}       BOOL searchResult = m_pRecordSet->Open(CRecordset::snapshot,szSQL,CRecordset::none);<span style="white-space:pre"></span>if(TRUE == searchResult)<span style="white-space:pre"></span>{<span style="white-space:pre"></span>return m_pRecordSet->GetRecordCount(); <span style="white-space:pre"></span>}<span style="white-space:pre"></span>else<span style="white-space:pre"></span>{<span style="white-space:pre"></span>return 0;<span style="white-space:pre"></span>}}BOOL Access::Qurey(LPCTSTR szSQL){/**在c++中,可以直接抛出异常之后自己进行捕捉处理, *可以在任何自己得到不想要的结果的时候进行中断, *比如在进行数据库事务操作的时候,如果某一个语句返回SQL_ERROR则直接抛出异常, *在catch块中进行事务回滚)**/TRY{<span style="white-space:pre"></span>m_Database.ExecuteSQL(szSQL);           //执行一条SQL语句。不返回数据记录}CATCH(CDBException,e){<span style="white-space:pre"></span>return FALSE;}END_CATCH;return TRUE;}CString Access::GetField(const SHORT fieldIndex){<span style="white-space:pre"></span>CString csTemp;//要存储字段的值 对象的引用<span style="white-space:pre"></span>m_pRecordSet->GetFieldValue(fieldIndex,csTemp);<span style="white-space:pre"></span>return csTemp;}CString Access::GetField(LPCTSTR fieldName ){<span style="white-space:pre"></span>CString csTemp;<span style="white-space:pre"></span>m_pRecordSet->GetFieldValue(fieldName, csTemp);<span style="white-space:pre"></span>return  csTemp;}下面我们再看看在按钮的响应函数中是如何实现对数据的操作的:void CExercise7Dlg::OnBnClickedBtnSelect()         // 连接测试{// TODO: Add your control notification handler code here<span style="white-space:pre"></span>Access *m_access;                              //构造ACCESS类的一个对象<span style="white-space:pre"></span>m_access = Access::GetInstance();<span style="white-space:pre"></span>if(TRUE == m_access->Connect(_T(".\\manage.mdb")))       // 判断是否成功连接数据库<span style="white-space:pre"></span>{<span style="white-space:pre"></span>MessageBox(_T("数据连接成功!"));<span style="white-space:pre"></span>}<span style="white-space:pre"></span>int nResult;                    //获取一个给定的字段值,并在消息盒中显示该字段值的第一行数据,意思数据连接成功。<span style="white-space:pre"></span>if((nResult = m_access->Select(_T("SELECT * FROM `manageTable`"))) > 0)<span style="white-space:pre"></span>{<span style="white-space:pre"></span>MessageBox(m_access->GetField((short)0).GetBuffer(256));<span style="white-space:pre"></span>MessageBox(m_access->GetField(_T("classes")).GetBuffer(256));<span style="white-space:pre"></span>}<span style="white-space:pre"></span>else<span style="white-space:pre"></span>{<span style="white-space:pre"></span>MessageBox(_T("数据库查询结果数为0"));<span style="white-space:pre"></span>}<span style="white-space:pre"></span>m_access->DisConnect();}void CExercise7Dlg::OnBnClickedBtnInsert()     // 插入数据测试{<span style="white-space:pre"></span>Access *m_access;<span style="white-space:pre"></span>m_access = Access::GetInstance();<span style="white-space:pre"></span>if(TRUE == m_access->Connect(_T(".\\manage.mdb")))<span style="white-space:pre"></span>{<span style="white-space:pre"></span>MessageBox(_T("数据连接成功!"));<span style="white-space:pre"></span>}<span style="white-space:pre"></span>CString strSQL;               // 此处为SQL语句的标准格式,详情请参考SQL语句。<span style="white-space:pre"></span>strSQL.Format(_T("INSERT INTO `manageTable`(`studynum`, `duty`, `name`, `task`, `classes`, `phone`, `qq`, `address`)\<span style="white-space:pre"></span>VALUES('%s','%s','%s','%s','%s','%s','%s','%s')"),<span style="white-space:pre"></span>TEXT("56"), <span style="white-space:pre"></span>TEXT("组员"), <span style="white-space:pre"></span>TEXT("大头"), <span style="white-space:pre"></span>TEXT("软件"), <span style="white-space:pre"></span>TEXT("机制0805"), <span style="white-space:pre"></span>TEXT("32432324234"), <span style="white-space:pre"></span>TEXT("32423"), <span style="white-space:pre"></span>TEXT("华中科技大学"));<span style="white-space:pre"></span>if(TRUE == m_access->Qurey(strSQL.GetBuffer(256)))<span style="white-space:pre"></span>{<span style="white-space:pre"></span>MessageBox(_T("插入数据成功"));<span style="white-space:pre"></span>}}</span>

下面的几个按钮的消息响应就类似啦,在此就不啰嗦了。

好了,至此MFC数据库的连接操作就此结束了,现在来做一个总结:

  1. 从这个函数中初窥了ODBC类API的封装及API接口的使用方法,这些方法其实是通用的准则,理解清楚这个,对后续的编程很有好处
  2. 从这个函数中,也进一步理解力C++中类的强大之处,通过ACCESS类对成员函数和成员变量的封装,在主对话框中,我们只需要调用ACCESS类的头文件,并创建一个类的对象,之后就可以利用这个对下对其成员进行调用。
  3. 这个函数中也用到了C++中的一些编程技巧,用好这些方法也是很重要的,如这里有:TRY,CATCH,END_CATCH、TRACE、Format等的使用 。