MFC ODBC

MFC ODBC 用法(转载)

 

 

主要内容:

 

MFC  ODBCODBC API封装在类CDatabaseCRecordSetCFieldExchangeCRecordViewCDBException

 

使用MFCODBC 开发数据库应用程序的一般步骤

 

使用AppWizard访问数据

 

使用类CDatabase连接数据

 

使用类CRecordSet开记录集、获取数据

 

使用类CRecordSet函数MoveFirst()MoveLast()MoveNext()MovePrev()IsBOF()IsEOF()进行记录集的遍 历

使用类CRecordSet函数AddNewUpdate加记

 

使用类CRecordSet函数EditUpdate修改记录

 

使用类CRecordSet函数Delete 删除记录

 

使用类CDatabase函数ExecuteSQL直接执行SQL命令

 

使用类CDatabase函数BeginTransCommitTransRollback处理事务

 

MFC OBDC技术

概述

 

MFCODBC类对较复杂ODBC API进行了封装,提供了简化的调用接口。

MFCODBC 类主要包括以下 5 个类:

 

 CDatabase类:主要功能是建立与数据源的连接


 CRecordset类:代表从数据源选择的一组记录(记录集)

 

CRecordView类:提供了一个表单视图与某个记录集直接相连,利用对话框数据交替机制(DDX在记录集与表单视图的控件之间传输数据

 

CFieldExchange类:支持记录字段数据交RFX即记录集字段数据成员与相应的数据库的表的字段之间的数据交换。

 

CDBException类:代表ODBC类产生的异常。 


CDatabase类操作数据源

CDatabase类型的对象表示一个到数据源的连接,通过它可以操作数据源。该类的成员函数如下表:

 

函数

说明

 CDatabase

构造一个对象

 Close

关闭数据源连接

 Open

通过一个 ODBC 驱动程序创建到数据源的连接

 OpenEx

通过一个 ODBC 驱动程序创建到数据源的连接

 

BeginTrans

开始事务

BindParameters

允许在调用 CDatabase::ExecuteSQL 前绑定参数

 Cancel

取消异步操作或第二条线程中的过程

 CommitTrans

执行事务

 ExecuteSQL

执行 SQL 语句,不返回记录

 Rollback

回滚事务,数据源返回先前的状态



 

 

 

 

该类的属性属性如下表:

 

属性

说明

 CanTransact

如果数据源支持事务,返回非零

 CanUpdate       

如果 CDatabase 可以更新,返回非零 

 GetBookmarkPersistence

获得书签对记录集对象的持久性

 GetConnect

返回 ODBC 连接串

GetCursorCommitBehavio r

获得提交事务对记录集对象的影响

GetCursorRollbackBehav ior

获得回滚事务对记录集对象的影响

 GetDatabaseName

返回当前使用的数据库名

 IsOpen

如果当前 CDatabase 对象连接到数据源,返回非零

 SetLoginTimeout

设置数据源连接的超时数(秒为单位)

 SetQueryTimeout

设置查询操作的超时数(秒为单位)

 


应用程序可使用多个 CDatabase 类型的对象。构造一个对象并调用 Open()

成员函数打开一个连接。接着构造CRecordset 类型的对象以操作连接的数据源,构造时向记录集对象传递 CDatabase 类型的指针。完成使用后,用 Close()成员函 数销毁 CDatabase 类型的对象。

 

一般情况下并不需要直接使用 CDatabase 类型的对象,因为 CRecordset 类型 的对象可以实现大多数的功能、但是在进行事务处理时,CDatabase 就起到关键 作用。事务(Transaction)指的是将一系列对数据源的更新放在一起,同时提交或一个都不提交,为的是确保多用户对数据源同时操作时的数据正确性。

 

CRecordset 类操作记录集

 

一个CRecordset 类型的对象代表从数据源选择的一组记录的集合——记录集,通过该类的方法实现对数据库中记录的各种操作。

 

该类常用的数据成员如下表:

 

成员

说明

m_hstmt

包含记录集的 ODBC 陈述句柄,类型为 HSTMT

m_nFields

包含记录集中字段数据成员的数量,类型为 UNIT

 m_nParams

包含记录集中参数数据成员的数量,类型为 UNIT

 m_pDatabase

包含一个 CDatabase 对象指针,通过它访问数据源

 m_strFilter

包含 CString 对象,定义 SQL 中 WHERE 子句

 m_strSort

包含 CString 对象,定义 SQL 中 ORDER BY 子句

 

 

 

 

该类的构造方法如下表:

 

构造方法

说明

 Close

关闭记录集和与之相关的 HSTMT

 CRecordset

构造一个 CRecordset 对象

 Open

通过获得表或执行记录集所代表的查询来打开记录集

 

 

 

 

CRecordset 类记录集属性如下表:

 

属性

说明

CanAppend

如果新记录可以通过 Addnew 添加到记录集,返回非零

CanBookmark

如果记录集支持书签,返回非零

 CanRestart

如果 Requery 可以被调用来再次运行记录集查询,返回非零

 CanScroll

如果可以在记录中回滚,返回非零

 CanTransact

如果数据源支持事务,返回非零

 CanUpdate

如果记录集可以被更新,返回非零

GetODBCFieldCount

返回记录集中字段的数量

GetRecordCount

返回记录集中记录的数量

 GetSQL

获得 SQL 字符串

 GetStatus

获得记录集的状态

 GetTableName

获得记录集所属的表名

 IsBOF

如果记录集定位在第一条记录之前,返回非零

 IsDeleted

如果记录集定位在一条删除的记录,返回非零

 IsEOF 

如果记录集定位在最后一条记录之后,返回非零       

 IsOpen

  如果调用过 Open 函数,返回非零

 

 

 

 

CRecordset 类更行操作如下表:

 

更新操作

说明

 AddNew

准备增加一条新纪录,调用 Update 之后完成添加

 CancelUpdate

取消任何未完成的更新

 Delete

从记录集中删除当前记录

 Edit

准备对当前记录进行修改,调用 Update 后完成修改

 Update

通过将新记录或编辑的数据存入数据源来完成 AddNew 或Edit 操作

 

 

 

 

记录集有两种形式:snapshot(表示数据的静态视图)和 dynaset(表示记

录集与其他用户对数据库的更新保持同步)。

CFieldExchange 类处理数据交换

 

 

CFieldExchange 类支持数据库类所使用的记录集字段交换RFX程式。如果使用自定义的数据类型写数据交换程式,会使用这个类。否则不会直接使用此类。RFX 在记录集对象的字段数据成员与数据源中当前记录的相应字段之间交换数据。

CRecordView 类显示记录

 

CRecordView 对象用于在控件中显示数据库记录的视图。这种视图是一种直 接连到一个 CRecordView 对象的格式视图,它从一个对话框模板创建资源,并将 CRecordView 对象的字段显示在对话框模版的控件里。对象利用DDX 和 RFX 机制, 使窗体上的空间和记录集的字段值之间数据移动自动化,也就是说,用户不需要编写一行代码就可以完成简单的数据库记录查看程序。

CDBException 类处理异常

 

由 CException 类派生,以 3 个继承的成员变量反映对数据库操作时的异常:


m_nRetCode:以 ODBC 返回代码(SQL_RETURN)的形式表明造成一场的原因

 

 m_strError:字符串,描述造成抛出异常的错误原因

 

 m_strStateNativeOrigin:字符串,用以描述以ODBC 错误代码表示的异常错误

 

 

使用MFC ODBC 编程建立应用程序

MFC  ODBC 编程模型概述

 

使用 MFCODBC 访问数据库比直接使用 ODBC API 简单得多,编程步骤如下:

 

使用CDatabase 打开数据源的连接,如果利用 AppWizard 生成一个 ODBC数据库应用程序,则会自动完成操作。

 

 使用 ClassWizard 向导加入由 CRecordset 类派生的用户记录集类,完成 对数据库的绑定。

 

创建记录集类对象,如果利用AppWizard 生成一个 ODBC 数据库应用程序,则会自动在文档类中创建。

 

 使用记录及对象对数据库进行遍历、增加、删除、修改等操作。

 

 使用CDatabase 类的 ExecuteSQL 函数直接执行 SQL 命令。

 

使用 CDatabase 类的 BeginTrans、CommitTrans 和 Rollback 函数进行事 务处理

 

使用CDatabase 类的 Close 函数关闭数据源连接。

通过 AppWizard建立数据库应用程序“New”-->MFC AppWizard(EXE)-->OK->Single document(单文

)-->NEXT-->选择需要对什么样的数据库类型支持做出选择:None(不要任 何数据支持,今后再添加很麻烦)Header files only(该工程需要数据库支持,但 不清楚细节时选择。工程会添加所要求的头文件和链接库,但必须自己在创建数据库类)Database view without file support(示包含数据库头文件和链接库,创建记录集和记录视图,应用程序虽有文档支持,但不支持串行化)Databaseviewwith  file support(表示包含数据库头文件、记录集和视图外,程序还支持串行化)-->Data Options  对话框:在Datasource中选择ODBC单选按钮,选择一个已经注册号的数据源(实例程序Sample中使用的数据源叫students)Recordsettype(记录类型)中有三个选项:Snapshot(快照:它是当前表的一 个静态视图。表打开之后,表中的所有数据马上被载入到应用程序中。其他用户或程序对表的修改只有在下次打开表时才会体现出来,看不到其他用户对表的即时修改,因此它是静态的。Snapshot适用于用户查询信息(例如生成报表等)而不适用于数据编辑)Dynaset(动 态集:这个选项创建指向所请球的每个记 录的实际指针。只有屏幕需要显示记录时,才从数据库中提取数据。这种方式的好处是动态、即时的浏览到当前的记录。而 其他用户也会即时看到你对记录所 做出的修改。该选项适合于创建用户要发费很多时间来编辑数据的应用程序,并且,如果正在编写大型数据库应用程序,他也是最  佳选择)Table(表:表方法(仅 使用 DAO访问数据库时可用)把所做查询的内容放进一个临时表。这样做不但减小了从服务器下载的信息量,还意味着程序员有了更大的灵活性,因为可以直接 操作临时表字段和记录。但缺点是你看不到别人的修改。使用DAO且用户会执行同等数量的数据查询和数据编辑时,它是最佳选择。)”  -->Selete  DatabaseTable(选择数据库表)”  -->Finish.通过AppWizard创建了一个 MFC ODBC数据库工程后,该工程与一般的应用 程序工程有所不同:多了一个CRecordset 的派生类,对应前面选择的数据库表; 视图类的基类是CRecordView负责显示数据;工具栏上多了遍历记录集的4个 按钮。

使用CDatabase类方法打开数据源

 

通过CDatabase类的Open 函数来打开数据源,该函数原型如下:

 

virtualBOOL  Open(

LPCTSTRlpszDSN, //一个数据源名,此数据源名是通过ODBC管理器注 册的。如果 DSN被设定在lpszConnect里,那lpszDSN不应在被重新设定lpszDSN应设为NULL。 如果没有设lpszConnect,而且又把lpszDSN设定 为 NULL那么将出现一个对话框,让用户选择数据 源。

BOOL bExclusive= FALSE, //默认为 FALSE表示以共享方式打开数据 源。当前版本的类库不支持独占方式,如果设定为TRUE,将失败


BOOL  bReadOnly= FALSE, //如果希望连接以只读方式打开,不想对数据源进行更新,那么设定为TRUE所有依靠此 连接打开的记录集全部继承此属性。默认值为FALSE


 LPCTSTRlpszConnect= _T(ODBC;),//连接串。连接串可能包含数据 源名、数据源中用户的ID、密码和 其他信息。整个连接串必须 以ODBC;开头。ODBC;表示连接是一个ODBC数据源。

 

BOOL  bUseCursorLib=TRUE //如果希望加载ODBC光标动态连接库, 设定为TRUE

 

);

 

下面的例子表示如何打开数据源:

 

//在文档类中加入

 

//CDatabase类对象

 

CDatabase  m_pdatabase;

 

//连接对象到一个数据源(没有密码),ODBC连接对话框将是中隐藏

 

m_pdatabase.Open(_T(students), FALSE, FALSE, _T(ODBC; UID= Admin));

//或者也可以显示ODBC对话框,请用户提供连接信息

 

m_pdatabase.Open(NULL);

 

像程序Sample 一样,通过向导生成数据库工程,不用添加代码就能实现对数据源的打开。在CRecordset类中有一个名为GetDefaultConnect()的虚函数值得 注意,通过调用它可以返回默认的数据源连接(也就是在生成工程的时候所选择 的数据源)来打开数据源。该函数如下:

 

CStringCSampleSet::GetDefaultConnect()

 

{

 

return_T(ODBC;  DSN= students);

 

}


使用CRecordste类打开记录集

 

通过声明 CRecordset记录集类的对象,再利用记录集类的Open()函数可打 开记录集,从而获取数据库中表的数据。也正是在调用Open()函数后,记录集当 中的成员变量得到数据源中表的字段值。Open()函数的形式如下:

 

virtual BOOL  Open(

 

UNIT nOpenType = AFX_DB_USE_DEFAULT_TYPE, LPCTSTR  lpszSQL = NULL,

DWORD  dwOptions  = none

 

);

 

其中 nOpenType为打开的类型,可取只有一下4 种:

 

CRecordset::dynaset:双向滚动的记录集,在记录集打开时,记录的 顺序和成员就被确定了。其他用户对数据的修改在fetch操作之后才可 访问,这也被叫做键集驱动的记录集。

 

CRecordset::dynamic双向滚动的记录集,其他用户对数据的修改在fetch操作后才可访问。许多ODBC驱动程序都不支持此种记录集。

 

CRecordset::ForwardOnly:只读记录集,只向前滚动。第二个参数lpszSQL是一个CString的指针,指向以下内容之一:

一个NULL指针;

 

一个表名;

 

一个SQL查询语句

 

第三个参数 dwOptions为一系列选项的组合,它的默认值为None。 可以像下面这样调用Open()函数来打开记录集

 

CDatabase  m_pdatabase;

 

m_pdatabase.Open(_T(students), FALSE, FALSE, _T(ODBC;UID=Admin));

 

CRecordset Sample(&m_pdatabase);

 

Sample.Open(CRecordset::dynaset,_T(Selectnamefrom students));


以上语句先用CDatabase对象打开一个数据源,之后构造记录及对象,最后记录及对象Sample以动态方式打students表中的name字段。

 

下面的语句表示以全部默认方式执行记录记得打开操作:

 

CDatabase  m_pdatabase;

 

m_pdatabase.Open(_T(students), FALSE, FALSE, _T(ODBC;UID=Admin));

 

CRecordset Sample(&m_pdatabase);Sample.Open();

也可以像下面代码中那样打开一个表中的所有字段:

 

CDatabase  m_pdatabase;

 

m_pdatabase.Open(_T(students), FALSE, FALSE, _T(ODBC;UID=Admin));

 

CRecordset Sample(&m_pdatabase);Sample.Open(CRecordset::dynaset, _T(Selectfrom students));

经过上面的操作,记录集对象的字段变量就获得了数据库中特定表中指定字

段的数据。

 

像程序Sample 一样,在向导当中选择数据源和表名。在CRecordset类中有 一个名为GetDefaultSQL()的虚函数值得注意,通过调用它可以返回默认的SQL语 句,用于形成记录集对象。该函数如下:

 

CString CSampleSet::GetDefaultSQL()

 

{

 

return _T([students]);

 

}

 

 

 

绑定记录集

 

通过向导创建工程后,程序的框架就生成出来。如果打开CRecordset的派 生类CSampleSet,会发现里面已经有了5个变量:


//Field/Param  Data

 

//{{AFX_FIELD(CSampleSet,  CRecordset)

 

long  m_id; CString  m_name; Long  m_department; Long  m_age;

CString m_comment;

 

//}}AFX_FIELD

 

5个变量正好与要访问的表中的字段同名,并且变量的类型也与字段类型 一致。这是MFC自动添加的变量,已绑定表中的字段。如果表中的字段是中文 的,那么MFC会创建m_column1m_column2等与之对应。RFX实现了这种绑 定。

 

RFXRecordField Exchange),记录字段交换使记录集(CREcordset)和隐 藏于后台的数据源(Datasource)之间建立其对应的关系。用户只需要操作记录 集,就可以实现对数据源的操作。MFC中提供了一组RFX 调用函数,利用这些 函数,就可以使记录集中的变量与数据源中的字段对应起来。使记录集和数据之 间进行数据交换成为可能,并且,这种交换是双向的。

 

可以在 CSampleSet类的DoFieldExchange函数中看到一组组的RFX 函数调用, 正是通过调用它们,使CSampleSet记录集中的变量与 students表中的字段对应 起来。

 

void  CSampleSet:: DoFieldExchange(CFieldExchange *pFx)

 

{//{{AFX_FIELD_MAP(CSampleSet)

 

pFX->SetFieldType(CFieldExchange::outputColumn);RFX_Long(pFX,  _T([id]), m_id);

RFX_Long(pFX, _T([name]), m_name);

 

RFX_Long(pFX, _T([department]), m_department);RFX_Long(pFX,  _T([age]), m_age);

RFX_Long(pFX, _T([comment]), m_comment);


//}}AFX_FIELD_MAP

 

}

 

RFX 函数通常有3个参数(个别的会有4 5个)。第一个参数为一个指向CFieldExchange类对象的指针,第二个参数为数据源中的一个字段名称,第三个 参数是与字段相对应的记录集中的变量名。常用的RFX 函数如下表:

 

函数

数据类型

 RFX_Bool

  BOOL

 RFX_Byte

 BYTE

 RFX_Binary

CByteArray

 RFX_Double

  Double

 RFX_Sing

 Float

 RFX_Int

 Int

 RFX_Long

  Long

 RFX_LongBinary

  CLongBinary

 RFX_Text

  CString

 RFX_Date

  CTime


参数化记录集和查询

 

CRecordset 类对象中有两个成员变量,一个为 m_strFilter(过滤字符串,负责对记录集进行过滤,返回过滤后的记录),另一个为 m_strSort(排序字符串,对 记录集进行排序)。

 

m_strFilter 存放着 SQL 语句中 WHERE 子句的条件字符串,m_strSort 中则 存放着 SQL 语句中ORDER BY 子句的字符串。经过对它们的赋值,可以更加灵活 的获得数据库中特定的数据,以及对记录进行排序。

 

下面的代码中向 m_strFilter 赋值“comment=good”,向 m_strSort 赋值 “name”:

 

CRecordset Sample;

 

Sample.m_strFilter = “comment = good”;


Sample.m_strSort = “name”;

 

除了直接向 m_strFilter 赋值外,还可以使用参数化。利用参数化可以更直观, 更方便地完成条件查询任务。使用参数化的步骤如下:

首先声明参变量:CString age1; CString comment1;

在构造函数中初始化参变量

 

age1  = _T(“”);

 

comment = _T(“”);

 

将参变量与对应列绑定

 

pFX->SetFieldType(CFieldExchange::param);

 

pFX->Text(pFX,  _T(“[age]”), age1);

 

pFX->Text(pFX,  _T(“[comment]”),  comment1);

 

最后利用参变量进行条件查询

 

m_pSet->m_strFilter = “age= ? AND  comment = ?”;

 

m_pSet->age  = “21”;

 

m_pSet->comment  = “good”;

 

m_pSet->Requery();

 

参变量的值按绑定的顺序替换查询字符串中的“?”适配符。代码中的 m_pSet 是 CRecordView 类的一个记录集指针,指向当前文档类中的记录集变量。它是在 CRecordView 类的 OnInitialUpdate 中被赋予文档类下记录集对象的指针的。下面是程序中 CSampleView 类的 OnInitialUpdate 函数体:

 

void  CSampleView::OnInitialUpdate()

 

{ //为 m_pSet 赋予文档类下的记录集对象的指针

 

m_pSet = &GetDocument()->m_sampleSet;CRecordView::OnInitialUpdate(); ResizeParentToFit();


}

遍历记录集合

 

CRecordset 类中有一组函数负责记录集指针的移动,例如使用记录集指针下移一个记录、使用记录集指针上移一个记录等。

 

1、 MoveFirst()函数:使指针移动到第一条记录

 

2、 MoveLast()函数:使指针移动到最后一条记录

 

3、 MoveNext()函数:使指针移动到下一条记录

 

4、 MovePrev()函数:使指针移动到前一条记录

 

5、 IsBOF()函数:当指针移动到第一条记录前面或者表中没有记录的时候 返回真

 

6、  IsEOF()函数:当指针移动到最后一条记录后面的时候返回真知道上面的这些函数的意义后,下面来看如何遍历记录集。下面的代码中

m_pSet 是一个记录集指针,m_list 为一个列表框控件(ClistBox 类)的变量:

 

if(!m_pSet->IsOpen()) //用 IsOpen 函数检测记录集是否打开

 

m_pSet->Open(); m_pSet->MoveFirst();while(!m_pSet->IsEOF())

{

 

m_list.AddString(m_pSet->m_name);

 

m_pSet->MoveNext();

 

}

 

m_pSet->MoveFirst(); //遍历完成后,使记录集指针指向第一条记录


书签定位和绝对定位

 

当在记录集中浏览的时候,可能想返回记录集中特定的一条记录,CRecordset 提供了两种方法可以指定记录集到特定的位置。

 

1、 书签定位


可以在记录集中的某一条记录增加一个书签。在记录集浏览时由于用户的增

删操作使记录的绝对位置发生改变,所以以来绝对位置是不可靠的。因此需要使 用书签定位来为所想要的记录定位。CRecordset 类中提供的书签定位的方法是

GetBookmark 和 SetBookmark 两个函数,他们的原型如下:

 

void  GetBookmark(CDBVariant& varBookmark); //参数为 CDBVariant 的对

 

void  SetBookmark(const CDBVariant& varBookmark);

 

这里只需直接使用 CDBVariant 的对象即可。

 

//创建 CDBVariant 对象

 

CDBVariant bookmark;

 

//rs是 CRecordset 类或 CRecordset 类派生类的对象

 

rs.GetBookmark(bookmark);

 

//一系列移动到其他记录的代码

 

rs.MoveNext(); rs.MoveNext(); rs.SetBookmark(bookmark);

GetBookmark 函数将当前的记录存入一个 CDBVariant 的对象中,经过一系

列的纪录移动之后,在调用 SetBookmark,并且用刚才记录“书签”的

CDBVariant 对象 bookmark 作参数来使用当前记录集重新指向“书签”的位置。

 

是否支持书签定位取决于 ODBC 驱动程序和记录集类型。可以通过调用 CRecordset::CanBookmark 来确定是否支持书签定位。如果想支持书签定位,还 需要在记录集的 Open 函数的 dwOptions 参数位置中加入 CRecordset::useBookmarks 参数。注意 forward-only recordsets(只向前)类型的记录集也不支持书签定位。还有一点,就是在某些记录集操作之后,也应该及时检查 前面所设置的“书签”是否还可以继续使用。例如,对一个记录及进行了 Requery 操作之后,书签就可能不再有效了。所以,在调用 SetBookmark 函数之前,应该 先调用CDatabase::GetBookmarkPersistence 函数来核对是否可以安全的调用 SetBookmark函数。下面是该函数原型:DWORD GetBookmarkPersistence() const;

 

这个函数的返回值为 bitmask,这是一个 DWORD 类型的返回值。该值可以 是下表中的多个 bitmask 值的组合。


 

bitmask 值

书签的有效性

 SQL_BP_CLOSE

Requery 操作后,书签有效

 SQL_BP_DELETE

对某行执行 delete 操作后,书签对此行依然有效

 SQL_BP_DROP

一次 Close 操作后,书签有效

 SQL_BP_SCROLL

任何 Move 操作之后,书签都有效

 SQL_BP_TRANSACTION

一次事务被提交或回滚后,书签有效

 SQL_BP_UPDATE

对某行执行 Update 操作后,书签对此行有效

 SQL_BP_OTHER_HSTMT

与某记录集对象相关的书签对另一记录集也有效

 

2、  绝对定位

 

相对于书签定位,绝对定位就好像记住某本书的某一个固定页码一样。绝对 定位就是通过原始的记录位置来设置当前记录,比如可以设置记录集中第 8 条记 录为当前记录。如想使用绝对定位来改变当前的记录集位置,可以调用 CRecordset::SetAbsolutePosition 函数。其原型为:

 

void  SetAbsolutePosition(long nRows);

 

nRows表示记录集中的一个绝对位置。调用该函数会把记录集指针定位到

nRows 参数所指行号的记录上。

 

下面的代码表示把记录集定位到第 12 条记录的位置上:

 

long  row; row = 12; rs.SetAbsolutePositon(row);

对于 ODBC 的记录集来说,绝对位置 1 指的是记录集当中的第一条记录,

绝对位置如果是 0 则代表的是 BOF 位置(在第一条记录之前)。

 

***forward-only recordsets(只向前)类型的记录集不支持SetAbsolutePosition方法。此外,记录的绝对位置存在潜在的不可靠性。如果用户删除了某一条记录,那么后续记录的位置都发上变化,并且记录集也可能被再 次重新创建,不能确保某条记录在创建的记录集中有与原来相同位置,因此建议使用书签定位。  ***


获取记录集的数据


通过在对话框上添加控件,并且为控件绑定变量来达到数据交换并显示的目

的,与控件绑定的变量正是记录集中的那些字段变量。步骤如下:

 

l按照所需显示的字段数量,在对话框窗体上添加几个编辑框控件,将控 件的ID改成与表中字段相似的名称

 

l再按住Ctrl的同时双击每一个控件,为它们增加成员变量

 

lAddMember Variable对话框中单击“Membervariable name”下拉框, 为每个控件一次选择记录集中的各个变量。

 

到底记录集中的变量是如何把数据显示在控件上呢?


下面讲DDX 技术

 

DDX DialogData Exchange的缩写,即对话框数据交换。它在对话框的控 件与记录集的变量之间建立起一座桥梁,可以使他们双向交换数据。

 

CRecordView 类里面有一个DoDataExchange 数,所有的DDX函数都是在 DoDataExchange中调用的。DoDataExchange函数为DDX 函数提供了一个CDataExchange类对象的指针。在工程刚刚创建时,CSampleView::DodataExchange函数是一个空函数,因此此时对话框上并没有控 件,更没有与控件对应的变量。但经过窗体添加控件、为控件添加变量之后,再打开DoDataExchange函数,将会出现下面这样的代码:

 

void  CSampleView::DoDataExchange(CDataExchange* pDX)

 

{ CRecordView::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CSampleView)

 

DDX_FieldText(pDX, IDC_ID,m_pSet->m_id,  m_pSet); DDX_FieldText(pDX,  IDC_NAME,  m_pSet->m_name,  m_pSet); DDX_FieldText(pDX, IDC_DEPARTMENT,  m_pSet->m_department,  m_pSet); DDX_FieldText(pDX, IDC_AGE,  m_pSet->m_age, m_pSet); DDX_FieldText(pDX, IDC_COMMENT,  m_pSet->m_comment,  m_pSet);

//}}AFX_DATA_MAP

 

}


DDX_FieldText 函数负责在对话框控件和记录集中字段变量之间建立联系

因为记录集中的字段变量对应的是数据库中表的每个字段,所以就能在控件上看到表中的数据了。DDX函数有四个参数,分别为:

 

l一个指向CDateExchange类对象的指针

 

l对话框上控件的ID

 

l记录集中要与对象绑定的字段变量

 

l记录集对象的指针

 

DDX 可以管理以下类型数据变量与控件的数据交换。它们是shortlongint

DWORDCStringfloatdoubleBOOLBYTE

 

程序通过 UpdateData这个函数在控件与变量之间达到双向数据,而不是直 接调用DoDataExchangeUpdateData函数的原型如下:

 

BOOL  UpdateData(BOOL bSaveAndValidate=TRUE);

 

bSaveAndValidate 指示了数据传输的方向,当为TRUE时就是控件向变量 传输。反之,就是变量向控件传输数据。默认值为TRUE


添加记录

 

对记录的操作大多数都是由 CRecordset类来负责的,执行添加的任务也不 例外。CRecordset类中的函数AddNew()表示向表中添加一条新的纪录,该函数 原型如下:

 

virtual void  AddNew();

 

执行 AddNew()函数之后会新增加一条空记录,等待输入数据。此时记录的 每一个字段都被初始化NULL。在输入新的记录数据之后,需调用另一个CRecordset类的函数才能完成对新记录的添加,这个函数是Update()函数原型 为:virtual BOOL Update();

 

在调用完 AddNew函数、输入新数据之后一定要调用Update,它负责把新 添加的数据保存到数据源。实际上AddNew数只是在内存中创建了一块缓冲 区,等待输入数据,之后需要使用Update来真正把数据存入数据源。如果在调 用Update之前滚动到了另一条记录,那么新记录就会丢失,也不会提出警告。 对于dynaset(动态集)类型的记录集,新记录会添加到末尾。新记录不能被添 加到Snapshot类型的记录集中。最后还需要调CRecordset::Requery函数以刷 新记录集:virtual BOOL Requery();


Requery 函数负责刷新记录集来反映当前最新的数据。在每次对记录进行添

加、删除后,都有必要调用Requery来更新记录集。调用Requery函数后,记录 集指针重新指向第一条记录。

 

下面的代码实现了在一个名为 OnAdd的函数中添加记录:

 

void  CSampleView::OnAdd()

 

{

 

//添加一条新记录

 

m_pSet->AddNew();

 

//对记录集中的m_idm_namem_departmentm_age等赋值

 

m_pSet->m_id = “1009”; m_pSet->m_name = “Jack”; m_pSet->m_department  = “3”; m_pSet->m_age  = “20”;

m_pSet->m_comment = “good”;

 

//更新记录集,将新记录存入数据源

 

m_pSet->Update();

 

//刷新记录集,并使记录集指针回到第一条记录

 

m_pSet->Requery();

 

}

 

 

 

删除记录

 

调用 CRecordset类中的Delete 函数进行删除记录。该函数用于删除当前记 录集指针指向的记录。原型如下:virtual void Delete();

 

一次成功的删除后,被删除记录的字段全部被设为 NULL,必须调用Move函数移动到其他记录上来一处被删除的记录。如果删除不成功,记录中的数据也 不会被破坏。一旦移除了删除的记录,就再也不能返回他了。在调用Delete 函 数时,记录集中必须有一条有效的记录,否则会产生错误。。如果Delete 了一


条记录,但没有 Move到另一条记录就有进行Delete操作Delete会产生一个

CDBException类的错误。

 

下面的代码在一个名为 OnDelete的函数中实现了删除当前记录的操作:

 

void  CSampleView::OnDelete()

 

{

 

//删除当前记录

 

m_pSet->Delete();

 

//刷新记录集

 

m_pSet->Requery();

 

}

 

 

 

修改记录

 

CRecordset::Edit 函数的作用是允许修改当前的记录。原型如下:

 

virtual void  Edit();

 

调用 Edit之后,就可以直接重新设定当前记录中每个字段的值了。再重新 设定之后,还需要调用 Update函数来保存对数据的修改。实际上,调用Edit之 后,要被修改的值先被保存起来。如果Edit之后没有Update,而是移动到另一 条记录,那么记录以前的值被重新恢复,不对记录作出修改。或者,调用了一次Edit,而对记录也作出了修改,然后又调用了一次Edit,那么记录还是被恢复到 第一次调用Edit之前的值。

 

下面的代码在名为 OnEdit的函数中实现对数据的编辑:

 

void  CSampleView::OnEdit()

 

{

 

m_pSet->Edit();

 

m_pSet->m_id = m_newid;

 

m_pSet->m_name = m_newname;

 

m_pSet->m_department = m_newdepartment;


m_pSet->m_age = m_newage;

 

m_pSet->m_comment = m_newcomment;

 

m_pSet->Update();

 

m_pSet->Requery();

 

}

直接执行 SQL语句(增加、删除表等)

 

并不是所有的 ODBC功能都被数据库类所支持,所以有时候需要使用直接 执行 SQL语句来对数据库进行一些操作。

 

CDatabase 类中有一个函数ExecuteSQL,通过它可以直接执行SQL语句, 对数据库进行操作。原型如下:void ExecuteSQL(LPCTSTR lpszSQL);

 

参数 lpszSQL是一个CString类型的指针,包含一条可执行的、有效地SQL 命令。这个函数并不返回信息,如果要对记录进行操作,那么还是要使用记录集 对象。

 

下面的代码可以在一个名为 class的表中增加一条记录。m_database是一个CDatabase类的对象VALUES中是新增记录的具体值,分别于表中的每一个字 段相对应。

 

m_pSet->Open();

 

m_pSet->AddNew();

 

m_database.ExecuteSQL(“insertinto class

 

VALUES(006‟,‟Mary‟,‟19‟,‟FEMALE‟)”);

 

m_pSet.Update();

 

 

 

 

m_pSet->Edit();

 

m_database.ExecuteSQL(“Delete from  class WHERE Name   = „Mary‟”);

 

m_pSet.Update();

 

 

 

事务处理


事务操作涉及 CDatabase类的几个成员函数,BeginTrans表示开始事务,

CommitTrans表示接受所有对数据源的修改,或者调用Rollback来终止整个事务。 执行事务的三个步骤:

 

l调用CDatabase类对象的BeginTrans成员函数开始事务

 

l调用AddNew->UpdateEdit->UpdateDelete等函数对同一数据库的一 个或者多个记录集进行一系列的添加、修改、删除操作

 

l调用CDatabase类的CommitTrans函数执行所有添加、修改、删除操作。如果一次更新出现错误,或者决定取消那些操作,调用Rollback函数。

 

下面的代码中要删除一家商店中的一项商品,因为要将两个记录集中涉及该商品的所有记录同时删除,所以要使用事务处理。记录集orderset为订单记录, 记录集 goodsset为商品品种记录。m_shopCDatabase类对象,假设它已经连 接到了数据源strgoodsID为用户输入的要删除商品的ID两个记录集中都使用m_strFilter变量来过滤出要删除的记录。

 

BOOL  CShopDoc::RemoveGoods(CString strgoodsID)

 

{//开始事务

 

if(!m_shop.BeginTrans())

 

return FALSE;

 

//创建订单记录集

 

COrderSet  orderset(&m_shop);

 

orderset.m_strFilter =“goodsID  =” + strgoodsID;

 

if(!orderset.Open(CRecordset::dynaset))

 

return FALSE;

 

//创建商品记录集

 

CGoodsSet goodsset(&m_shop);

 

goodsset.m_strFilter =“goodsID  =” + strgoodsID;

 

if(!goodsset.Open(CRecordset::dynaset))

 

return FALSE;

 

//操作中使用了trycatch()来捕获错误信息


//因为有时会经常出现一些意想不到的错误

 

TRY

 

{//删除订单记录

 

while(!orderSet.IsEOF())

 

{ orderset.Delete();orderset.MoveNext();

}

 

//删除商品条目

 

goodsset.Delete();

 

//执行事务

 

m_shop.CommitTrans();

 

} CATCH_ALL(e)

{//取消事务

 

m_shop.Rollback();

 

return FALSE;

 

} END_CATCH_ALL

//关闭记录集

 

orderset.Close(); goodsset.Close();returnTRUE;

}


使用多记录集

 

有时候需要使用多个记录集,以使在一个程序中可以操作多个表。生成工程 时,通常只选择一个表作为记录集要对应的对象。而且就算在生成工程时选择多 个表,那么AppWizard也只会产生多个表的一个笛卡尔乘积,并不会为每一个表产生一个记录集。

 

通过 Visual C++ 6.0ClassWizard来为所需要的每个表创建一个与之对应 的记录集。

 

View”à  “ClassWizard”à  “Add Class”à  “New”  à  “New  Class对话 框填入NameBaseClass”中选择  CRecordset 单击OKàDatabaseOptions” 选择ODBC 作为数据源,在下拉框中选择已注册的某个数据源,Recordset type 选择 dynaset,单击OK”  à  “Select  Database Tables对话框,在其中选择想 要访问的表后单击OK这样一CRecordset类的派生类就创建好了。在需要使 用该派生类的类中#include“***.h”(该派生类的同文件)


复制 搜索

你可能感兴趣的:(MFC,mfc,数据库,delete,sql,exchange,文档)