MFC ODBC 用法_clongbinary odbc-程序员宅基地

技术标签: 文档  null  mfc  数据库  delete  sql  

主要内容:

MFC ODBCODBC API封装在类CDatabaseCRecordSetCFieldExchangeCRecordViewCDBException

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

使用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连接串

GetCursorCommitBehavior

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

GetCursorRollbackBehavior

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

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对象,定义SQLWHERE子句

m_strSort

包含CString对象,定义SQLORDER 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

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

 

记录集有两种形式:snapshot(表示数据的静态视图)和dynaset(表示记录集与其他用户对数据库的更新保持同步)。

CFieldExchange类处理数据交换

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

CRecordView类显示记录

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

CDBException类处理异常

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

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

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

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

 

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

MFC ODBC编程模型概述

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

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

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

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

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

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

使用CDatabase类的BeginTransCommitTransRollback函数进行事务处理

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

通过AppWizard建立数据库应用程序

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

通过AppWizard创建了一个MFC ODBC数据库工程后,该工程与一般的应用程序工程有所不同:多了一个CRecordset的派生类,对应前面选择的数据库表;视图类的基类是CRecordView,负责显示数据;工具栏上多了遍历记录集的4个按钮。

使用CDatabase类方法打开数据源

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

virtual BOOL Open(

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

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

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

LPCTSTR lpszConnect _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()的虚函数值得注意,通过调用它可以返回默认的数据源连接(也就是在生成工程的时候所选择的数据源)来打开数据源。该函数如下:

CString CSampleSet::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(Select name from 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(Select from 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实现了这种绑定。

RFXRecord Field 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个参数(个别的会有45个)。第一个参数为一个指向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_pSetCRecordView类的一个记录集指针,指向当前文档类中的记录集变量。它是在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类中提供的书签定位的方法是GetBookmarkSetBookmark两个函数,他们的原型如下:

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

void SetBookmark(const CDBVariant& varBookmark);

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

//创建CDBVariant对象

CDBVariant bookmark;

//rsCRecordset类或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方法。此外,记录的绝对位置存在潜在的不可靠性。如果用户删除了某一条记录,那么后续记录的位置都发上变化,并且记录集也可能被再次重新创建,不能确保某条记录在创建的记录集中有与原来相同位置,因此建议使用书签定位。 ***

获取记录集的数据

通过在对话框上添加控件,并且为控件绑定变量来达到数据交换并显示的目的,与控件绑定的变量正是记录集中的那些字段变量。步骤如下:

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

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

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

到底记录集中的变量是如何把数据显示在控件上呢?下面讲述DDX技术。

DDXDialog Data 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函数有四个参数,分别为:

一个指向CDateExchange类对象的指针

对话框上控件的ID

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

记录集对象的指针

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

程序通过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(insert into 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来终止整个事务。执行事务的三个步骤:

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

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

调用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();

return TRUE;

}

 

 

使用多记录集

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

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

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

原地址: http://conanswp.blog.163.com/blog/static/4100770520114135453935/
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/g8gerrard/article/details/7909674

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签