前言
ODBC(Open Database Connectivity,开放式数据库连接)提供了一种统一访问数据库的接口,MFC ODBC数据库类将ODBC API函数进行了封装,可以避免直接使用ODBC API时大量代码的编写,大大简化了ODBC数据库开发的编程工作。用于在数据源和记录集对象之间进行数据交换的::SQLBindCol函数在MFC ODBC下也由RFX()所代替。由于在MFC ODBC数据库编程中很少需要开发人员直接对该函数进行修改,因此许多开发人员往往忽略了RFX函数在程序中的作用,至于该机制的工作原理和方式也往往没 有深刻的理解。这如同于Windows平台下的消息机制,在MFC下如果对消息的概念不甚了解虽然也还可以编程,但由于对消息没有建立深刻的认识是不可能 写出很出色程序的。因此,对于在数据库编程中起重要做用的RFX机制以及用于批量记录字段交换的Bulk RFX机制有必要做一个全面深入的认识。
RFX机制分析
RFX(Record Field Exchange,记录字段数据交换)在用户选择的记录集(Data set) 和隐藏于后台的数据源(Data source)之间建立一一对应关系,使用户能通过操作此记录集来实现对后台数据源的操作。在工作原理上RFX与用来在对话框的可视控件 (Controls)和成员变量(Member variables)之间建立双向对应关系的DDX(Dialog Data Exchange,对话框数据交换)比较类似。
总的来说,RFX主要是同记录集对象和数据源打交道的。用户选定的记录集对象的字段数 据成员所组成的编辑缓冲区包含了当前记录中被选定的列数据。记录集被打开时,自动定位于第一个行集,这时每一个被选中的列通过RFX与相应的字段数据成员 的地址绑定在一起。当记录集将某记录更新后,RFX调用ODBC API函数发送SQL UPDATE语句,将字段数据成员的值写入相应的列。除了对数据源与记录集字段数据成员之间的数据交换进行管理之外,RFX还负责着对参数绑定的管理:参 数数据成员在记录集被打开的同时会以在SQL语句中"?"占位符出现的顺序被绑定。
1、记录字段交换过程
在进行 打开、滚动、编辑以及删除记录操作的时候,RFX将根据不同的用户操作来采取不同的操作顺序,DoFieldExchange也将被调用以执行若干相应的 操作。在用记录集对象的Open函数时,需要将列和参数进行绑定。如果记录集中含有参数数据成员,将调用DoFieldExchange来把参数按顺序绑 定到SQL语句的参数占位符"?"上。而且在执行SQL语句前,对SELECT语句的每个参数都要进行数据类型的检查。发送完SQL语句后将再次调用 DoFieldExchange来绑定参数数据成员和字段数据成员。之后ODBC将完成记录的移动和数据的填充并通过DoFieldExchange把数 据转换成C++的类型。在进行随后的编辑操作时,编辑记录涉及到改变记录集字段数据成员的值。从RFX角度来看,该操作过程首先是调用记录集对象的 Edit函数,此时RFX将当前编辑缓冲区的值储存起来,在重建的时候将会用到。
在Edit完成了对编辑缓冲区的字段准备后可以直接 编辑或是填充需要改变字段的值,这其中也包括使用SetFieldNull函数将字段设置为空值。最后将调用Update函数以检查字段的数据成员,如果 数据发生了改变,Update将执行一条SQL INSERT语句,包含了记录集中被更改的字段值。RFX将检测发生改变的字段数据成员,并当Update函数调用时,RFX将每个数据成员值与 ESEUDO_NULL进行比较。如果不相同,则该数据成员将被设置。
至于滚动记录和删除记录时RFX的操作则要简单的多,在用 MoveNext之类的函数滚动记录集时,将通过DoFieldExchange函数来改变字段数据成员的值。而在删除记录时,RFX将会把所有的字段值 设置为NULL,以标识该记录已经被删除,但并不将该记录备份到缓冲区。因此必须通过移动记录指针才能将该条记录移除。
2、RFX功能代码
在MFC里提供了一系列RFX 调用函数,这些RFX函数负责在数据源的数据表列与记录集对象的字段数据之间交换数据,这种交换是可以双向进行的。一般是通过使用ClassWizard 在记录集类的DoFieldExchange成员函数中自动编写RFX函数调用的,这类函数有一个指向CFieldExchange类对象的指针,只需简 单的传递指向CFieldExchange的 pFX指针给DoFieldExchange即可。DoFieldExchange函数被频繁的提及,其实该函数可以说是RFX的中枢,任何时候程序框架 需要在数据源和记录集之间进行任何数据交换都必须通过该函数的中介才能完成,其作用同用来进行对话框数据交换的DoDataExchange()有些类 似。该函数的典型代码如下:
void CMyDB::DoFieldExchange(CFieldExchange* pFX) { //{{AFX_FIELD_MAP(CSections) pFX->SetFieldType(CFieldExchange::outputColumn); RFX_Text(pFX,"序号",m_strID); …… //}}AFX_FIELD_MAP } |
其中"//{{AFX_FIELD_MAP"中的代码被成为字段映射,通过RFX函数来处理各个字段。DoFieldExchange()函 数执行时,通过pFX指针调用CFieldExchange类成员函数SetFieldType(),这指定了直到DoFieldExchange函数末 尾或下一次SetFieldType调用范围内的所有RFX函数都是用于字段处理(ouput columns)的。在DoFieldExchange中为每一个字段数据成员都根据数据类型的不同而调用相应的RFX函数,这些调用指定了数据源的列与 字段数据成员之间的对应关系,同时,它们之间的数据交换任务也是由这些RFX函数完成的。根据数据类型的不同,提供有不同的RFX函数:
RFX函数 |
数据交换类型 |
RFX_Binary |
转换CbyteArray型的字节数组数据 |
RFX_Bool |
转换布尔型数据 |
RFX_Byte |
转换单字节数据 |
RFX_Date |
使用CTime或TIMESTAMP_STRUCT转换时间或日期 |
RFX_Double |
转换双精度数据 |
RFX_Int |
转换整型数据 |
RFX_Long |
转换长整型数据 |
RFX_LongBinary |
使用CLongBinary对象转换大型二进制数据 |
RFX_Text |
转换字符串数据 |
RFX_Single |
转换单精度数据 |
至于为RFX提供支持的CFieldExchange类,在一般情况下是不需要程序开发人员进行直接干预的,但在编写自定义的数据交换模块时 可以手工编写调用该类的代码。该类在功能上提供了记录数据交换或多行数据交换所需要的上下文环境,而且还支持诸如绑定参数和字段数据成员等操作。
Bulk RFX机制分析
MFC ODBC数据库类提供了对多行存取的支持,这种从数据源一次取回多个记录的Bulk RFX(批量记录字段交换)机制在大数据量的情况下效率是比较高的。但需要特别注意的Bulk RFX只能实现从数据源到记录集对象的单向数据流动。在用Open函数打开记录集对象之前,可以用SetRowSize()来定义行集的大小,缺省状态是 25条。在使用Bulk RFX的Open函数时必须将dwOptions参数指定为CRecordset::useMultiRowFetch。虽然使用多行存取可以在某种程度 上提高程序的性能,但在具体使用时必须要考虑到以下几个细节,否则将会导致失败或不可预料的错误:
首先,程序提供的DoBulkFieldExchange成员函数只提供从数据源到记录集对象的单向数据传输,因此用户不能使用诸如AddNew()、Edit()和Update()等试图写往数据源的函数。
其次,不能使用IsDeleted、SetFieldDirty等成员函数,但在编程中可以采取一些变通的方法,比如可以用GetRowStatus来代替IsDeleted。
最后需要特别注意的是,对记录集的指针的移动必须以行集为单位,这是比较容易理解的。总的来说Bulk RFX机制同RFX是非常相似的,使用的Bulk RFX函数也基本上是在原RFX函数的基础上扩充具备了多行存取的功能。
小结
本文主要对MFC ODBC数据库编程中数据源和数据集对象间数据交换这一环节展开讨论,并重点在概念和使用上对这一环节的RFX和用于多行存取的Bulk RFX机制进行了介绍。通过本文使程序开发人员能够对RFX和Bulk RFX的数据交换机制有一个比较好的认识。
From: http://blog.csdn.net/workdog/archive/2007/03/08/1524126.aspx