MFC ODBC数据库类

       1.ODBC概述

      数据库应用程序是指能够通过数据库管理系统DBMS访问数据库的程序。Visual C++提供了多种数据库访问技术,其中经常使用的开放式数据库连接(open database connectivity,ODBC)和数据存取对象(data access object,DAO)两种关系数据库访问技术。ODBC是一个技术上成熟可靠的标准接口,基本上可用于所有的关系数据库。

     ODBC是微软开放服务结构(Windows open services architecture,WOSA)中有关数据库一个组成部分,它建立了一组数据库访问规范,为用户提供了简单、标准、透明和统一的数据库访问的编程接口(即API),使应用程序独立于DBMS。ODBC API作为数据库的一种底层访问技术,它支持SQL(structured query language)语言,并且用户可以直接将SQL语句提交给ODBC API。

     ODBC的体系结构包括如下4个组件。

     (1)应用程序,执行处理并调用ODBC API函数,以提交SQL语句并获取结果。

     (2)驱动程序管理器(driver manager):根据应用程序的需要加载或卸装驱动程序,处理ODBC API函数调用,或将函数调用转交给ODBC驱动程序。驱动程序管理器包括一组ODBC API函数,它们位于ODBC32.dll动态链接库中。

     (3)ODBC驱动程序(driver):处理ODBC API函数调用,提交SQL请求到一个指定的数据源,并把结果返回给应用程序。ODBC驱动程序通常是一个DLL。

     (4)数据源(data source):应用程序要连接一个数据库,首先必须设置一个数据源。一个数据源包含了用户要访问的数据库及相关的DBMS、网络平台等信息,ODBC驱动程序管理器根据数据源提供的信息,建立ODBC与具体数据库的联系。数据源是应用程序的操作对象,应用程序通过数据源就能找到对应的数据库物理文件。

      一个ODBC应用程序对数据库的操作不依赖于具体的DBMS,不直接与DBMS打交道,所有的数据库操作由对应的DBMS的ODBC驱动程序完成。例如,对于Access、SQL Server和Oracle等关系数据库管理系统,用户均可用ODBC API进行访问。

      需要注意的是,ODBC API并不能直接访问数据库。借助于ODBC.INI文件,驱动程序管理器负责将应用程序对ODBC API的调用传递给对应的ODBC驱动程序,由驱动程序完成相应的操作。

      2.MFC ODBC数据库类

      ODBC提供了访问DBMS的统一接口,但是直接使用ODBC API创建数据库应用程序需要编制大量的代码。MFC的ODBC类对复杂的ODBC API进行了封装,揭供了简化的调用接口,从而大大方便了数据库应用程序开发。如果要对数据库底层进行操作,在MFC ODBC数据库应用程序中也可以直接调用ODBC API。

      MFC的ODBC数据库类主要包括如下内容。

      (1)CDatabase:主要功能是建立与数据源的连接。

      (2)CRecordset:从数据源中撮的记录集。CRecordset类的对象有动态行集(dynasets)和快照集(snapshots)两种形式,每一种形式在记录集被打开时提供一组记录。动态行集能保持与其他用户所做的更改同步,而快照集则是数据的一个静态视图。

      (3)CRecordView:CRecordView是连到一个CRecordset对象的表单视图,它是CFormView类(一个基于对话框的视图类)的派生类,用于显示数据库记录。利用对话框数据交换机制DDX在记录与表单视图的控件之间传输数据。

      (4)CFieldExchange:支持记录字段数据交换RFX(record field exchange),即记录集字段数据成员与相应的数据库的表的字段之间的数据交换。

      (5)CDBException:用于处理ODBC类产生的异常。

      从上面可以看出,CDatabase针对某个数据库,它负责连接数据源;CRecordset针对数据源中的记录集,它负责对记录的操作;CRecordView负责界面;而CFieldExchange负责CRecordset与数据源的数据交换。

      3.编程实现

      编写一个基于MFC的数据库应用程序一般分为三个步骤,首先利用ODBC数据源管理器配置与数据库对应的数据源,然后利用MFC AppWizard应用程序向导生成数据库应用程序框架,最后根据需要向框架添加具体的源代码。下面通过一个例子说明数据库应用程序的设计原理、方法和步骤。

      例:编写一个基于MFC的数据库应用程序Myodbc,实现简单的记录浏览功能,并支持记录的修改、增加和删除功能。

      本例程序要访问一个Access数据库APETEST.MDB,需要利用ODBC数据源管理器配置数据源,安装对应的驱动程序。打开Windows控制面板中的管理工具,执行“数据源ODBC”,出现如图1所示的ODBC数据源管理器。用户可以定义三种类型DSN(data source name)。


  

     

  •      用户DSN:当前用户配置的数据源,只有当前用户可以使用。
  •      系统DSN:系统配置的数据源,有访问权限的用户都可以使用。
  •      文件DSN:由一个文本文件定义的数据源,安装了驱动程序的用户可以使用。

      在“用户DSN”页面单击“添加”按钮,弹出“创建新数据源”对话框。在该对话框中选择Access数据库对应的驱动程序即Microsoft Access Driver(*.mdb)项,然后单击“完成”按钮。接下来弹出“ODBC Microsoft安装”对话框,在对话框中输入用户自已命名的一个数据源名(如APETEST)和说明字符串,然后单击“选择”按钮,在随后弹出的对话框中通过路径“...\Microsoft Visula Studio\Common\Tools\APE”找到并选择APETEST.MDB。单击“确定”按钮后,一个名为APETEST的数据源就被配置成功了。

  以上介绍了利用ODBC数据源管理器配置Access数据源的方法,其他类型数据库如SQL Server数据源的配置方法有所不同。有时在一个应用程序中需要连接不同的数据源,这种手工配置数据源的方法不太实用。因此,在程序中常常通过ODBC API动态配置数据源,这时需要调用ODBC API函数SQLConfigDataSource()。由于Visual C++的默认库文件中不包含该函数,因此使用该函数前需要包含头文件Odbcinst.h,并执行Project|Settings命令,在Link碰面的Object/library modules编辑框中输入导入库文件Odbccp32.lib,同时保证系统目录System32下有文件odbccp32.dll。

      (2)生成Myodbc应用程序框架。

      利用MFC AppWizard应用程序向导可以自动生成一个基于MFC的数据库应用程序框架。首先创建一个单文档应用程序Myodbc,在向导的第二步列出了关于数据库操作四个选项,本例仅想读写数据库记录而不需要读写文档(序列化),因此选择Database view without file support。然后单击Data Source按钮,出现Database Options对话框,旬出了已配置的数据源。选择要操作的数据源,如APETEST,单击OK按钮后出现Select Database Tables对话框,其中列出了所选数据源中包含的数据库表。选择希望操作的表,如OrderDetails,单击OK按钮。

      与一般应用程序不同的是:MFC AppWizard向导创建了一个数据库有关的类CMyodbcSet,它记录集类CRecordset类的派生类;视图类CMyodbcView是表单视图CRecordView的派生类,负责显示记录。应用程序框架通过CDatabase对象自动进行数据源的连接和关闭,这些代码对用户是透明的,在源程序中并没有出现。

      MFC AppWizard向导还自动创建了一些类的数据成员。在生成的CMyodbcDoc类中有一个CMyodbcSet对象的m_myodbcSet,它随文档对象的建立而自动建立,随文档对象的删除而自动删除。注意,在生成的CMyodbcView类中还包含一个指向CMyodbcSet对象的指针m_pSet,在视图中通过该指针直接访问数据库,在表单视图和记录集之间建立联系,使得记录集中的查询结果能够方便地在表单视图中显示出来。

      (3)增加浏览记录集和修改记录功能。

      要真正实现浏览和修改记录的功能,还需要用户添加具体代码。打开工作区的资源视图,找到一个ID为IDD_MYODBC_FORM的对话框模板,表单视图用该查模板来显示记录。向对话框加入静态文本控件和对应的编辑控件。根据数据库表文件OrderDetails的五个字段名,将对应的五个编辑控件的ID为IDC_ORDERID、IDC_PRODUCTID、IDC_UNITPRICE、IDC_QUANTITY和IDC_DISCOUNT。

     接下来,利用ClassWizard把表单视图中的编辑控件与记录集的域数据成员连接起来,实现控件与当前记录的DDX数据交换。启动ClassWizard,选择Member Variables页并选择CMyodbcView类。在控件列表中分别双击IDC_ORERID、IDC_PRODUCTID等五项,打开Add Member Variable对话框。注意该对话框的Member variable name显示的是一个组合框,而不是平常看到的编辑框。可以在组合框列表中选择合适的域数据成员作为变量,这里分别选择m_pSet->m_OrderID、m_pSet->m_ProductID等。

    编译、链接并运行Myodbc,就会发现MyOdbc是一个不错的记录浏览器,并且用户可以对记录时进行修改。Myodbc的运行界面图2所示。


图2 Myodbc的运行界面


 图3 OrderDetails表的结构

     一般的DDX都是在控件和控件父窗口的数据成员之间交换数据,而表单视图则是使用DDX在控件和一个外部对象(CRecordset的派生类对象)之间交换数据。读者可以查看CMyodbcView的DoDataExchange()成员函数,该函数是与m_pSet指针指向的记录集对象的域数据成员交换数据,交换数据的代码是ClassWizard类向导自动加入的。图4描述了DDX和RFX(record field exchange)数据交换机制。RFX数据交换通过成员函数CMyodbcSet::DoFieldExchange()来实现。

 

     CRecordView提供了对ID_RECORD_FIRST、ID_RECORD_LAST、ID_RECORD_NEXT和ID_RECORD_PREV等四个命令的支持,用于滚动记录。它的成员函数OnMove()处理这四个命令消息,OnMove()函数对用户是透明。

BOOL CRecordView::OnMove(UINT nIDMoveCommand)
{
	CRecordset* pSet=OnGetRecordset();
	if(pSet->CanUpdate())
	{
		pSet->Edit();		//进入编辑模式
		if(!UpdateData())	//将控件中的数据更新到记录集对象的域数据成员中
			return TRUE;
		pSet->Update();		//将域数据成员的值写入数据源
	}
	switch(nIDMoveCommand)	//分别处理四个不同的命令
	{
		case ID_RECORD_PREV:
			pSet->MovePrev();
			if(!pSet->IsBOF())
				break;
		case ID_RECORD_FIRST:
			pSet->MoveFirst();
			break;
		case ID_RECORD_NEXT:
			pSet->MoveNext();
			if(!pSet->IsEOF())
				break;
			if(!pSet->CanScroll())	//clear out screen since we're sitting on EOF
			{
				pSet->SetFieldNull(NULL);
				break;
			}
		case ID_RECORD_LAST:
			pSet->MoveLast();
			break;
		default:			//Unexpected case value
			ASSERT(FALSE);
	}//switch
	//Show results of move operation
	UpdateData(FALSE);			//把新的当前记录的内容置入到表单视图的控件中
	return TRUE;
}//OnMove()

      在函数的多分支语句中调用了CRecordset类各种用于滚动记录的成员函数,这些函数在滚动到一个新的记录时会把该记录传递到数据成员中。OnMove()函数一来一回完成了两次表单控件和数据源的数据交换,即控制DDX和RFX数据交换。

      (4)支持记录的增加和删除

      对数据库的操作除了对记录的浏览和修改,还包含对记录的增加和删除。下面为Myodbc程序添加记录的增加和删除功能,并支持数据库异常的处理。在Myodbc程序Record菜单中增加三个菜单命令项;Add、Delete和Refresh。菜单ID分别为ID_RECORD_ADD、ID_RECORD_DELETE和ID_RECORD_REFERSH,它们分别来添加、删除和刷新记录。

      在处理增加、刷新和滚动命令时必须有一个标志来判断当前是否处于增加模式,以便向数据源中加入新记录或进行普通的滚动处理。在CMyodbcView类定义中声明一个protected属性、BOOL类型的变量m_bAddMode,并在构造函数中初始化。

CMyodbcView::CMyodbcView()
	: CRecordView(CMyodbcView::IDD)
{
	//{{AFX_DATA_INIT(CMyodbcView)
	m_pSet = NULL;
	m_bAddMode=FALSE;	//初始化变量
	//}}AFX_DATA_INIT
	// TODO: add construction code here
}

      利用ClassWizard为上面Add、Delete和Refresh三个命令添加消息处理函数:

void CMyodbcView::OnRecordAdd() 
{
	// TODO: Add your command handler code here
	if(m_bAddMode)					//如果已处于增加模式,则完成添加记录操作
		OnMove(ID_RECORD_FIRST);	//滚动到别的记录上,新记录被保存到数据源中
	m_pSet->AddNew();				//进入增加模式
	m_bAddMode=TRUE;
	UpdateData(FALSE);				//更新表单视图
}


void CMyodbcView::OnRecordDelete() 
{
	// TODO: Add your command handler code here
	TRY
	{
		m_pSet->Delete();
	}
	CATCH(CDBException,e)
	{
		AfxMessageBox(e->m_strError);
		return;
	}
	END_CATCH
	m_pSet->MoveNext();		//滚动到下一个记录
	if(m_pSet->IsEOF())		//如果滚出了记录集的边界,则滚动到最后一个记录
		m_pSet->MoveLast();
	if(m_pSet->IsBOF())		//如果记录变空了,则清除域数据成员
		m_pSet->SetFieldNull(NULL);
	UpdateData(FALSE);		//更新表单视图
}

void CMyodbcView::OnRecordRefresh() 
{
	// TODO: Add your command handler code here
	if(m_bAddMode==TRUE)
	{
		m_pSet->Move(AFX_MOVE_REFRESH);		//取消增加模式开始恢复域数据成员的原值
		m_bAddMode=FALSE;
	}
	UpdateData(FALSE);			//恢复表单视图中的记录
}

     另外,需要为表单视图编写新的OnMove()函数来处理滚动命令,因为原来的OnMove()函数没有增加记录的功能。利用ClassWizard类向导重载OnMove()虚函数,添加如下代码。

BOOL CMyodbcView::OnMove(UINT nIDMoveCommand) 
{
	if(m_bAddMode)		//判断当前是否处于增加模式,以便添加记录或进行普通的滚动
	{
		if(!UpdateData())
			return FALSE;
		TRY
		{
			m_pSet->Update();		//把新记录保存到数据源
		}
		CATCH(CDBException,e)
		{
			AfxMessageBox(e->m_strError);
			return FALSE;
		}
		END_CATCH
		m_pSet->Requery();		//重新查询,使新加的记录对用户可见
		UpdateData(FALSE);		//更新表单
		m_bAddMode=FALSE;
		return TRUE;
	}
	return CRecordView::OnMove(nIDMoveCommand);
}

     CMyodbcView::OnMove()函数负责处理滚动命令。与CRecordView::OnMove()函数不同,该函数对增加模式下的滚动重新进行了处理;在调用CRecordset::Update()函数把新记录保存到数据源后,调用CRecordset::Requery()函数重新查询记录集。因为Myodbc使用的是快照型记录集,快照不反映用户增加记录,所以需要调用Requery()函数把新的记录加入记录集中。在调用Requery()函数后,会自动滚动到第1条记录上。在调用CRecordset::Update()函数时,对可能发生异常进行了处理。

     在Add命令的处理函数OnRecordAdd()中调用了CRecordset::AddNew()时进行增加模式。如果已处于增加模式,调用CMyodbcView::OnMove()函数滚动到别的记录这将使新记录保存数据源中。通过滚动完成记录的增加是一种简便实用的方法。

     在Delete命令的处理函数OnDelete()中调用了CRecordset::Delete()来删除记录,并进行了异常处理。在调用Delete()后,滚动记录到新的位置以跳过被删除的记录。

     Refresh命令的处理函数OnRefresh()用来放弃修改添加记录的操作。如果当前处于增加模式,则调用CRecordset::Move(AFX_MOVE_REFRESH)取消增加模式并恢复域数据成员的原值。调用CRecordView::UpdateData(FALSE)恢复表单视图中的记录。

     编译、链接后创建了应用程序,程序Myodbc实现了记录的增加和删除功能。

 

你可能感兴趣的:(数据结构,应用服务器,配置管理,网络应用,mfc)