《4使用MFCODBC访问数据库.docx》由会员分享,可在线阅读,更多相关《4使用MFCODBC访问数据库.docx(28页珍藏版)》请在三一办公上搜索。
1、4使用MFCODBC访问数据库主要内容: MFC ODBC将ODBC API封装在类CDatabase、CRecordSet、CFieldExchange、CRecordView和CDBException中 使用MFC ODBC开发数据库应用程序的一般步骤 使用AppWizard访问数据库 使用类CDatabase连接数据库 使用类CRecordSet打开记录集、获取数据 使用类CRecordSet的函数MoveFirst、MoveLast、MoveNext、MovePrev、IsBOF和IsEOF进行记录集的遍历 使用类CRecordSet的函数AddNew和Update增加记录 使用类CR
2、ecordSet的函数Edit和Update修改记录 使用类CRecordSet的函数Delete删除记录 使用类CDatabase的函数ExecuteSQL直接执行SQL命令 使用类CDatabase的函数BeginTrans、CommitTrans和Rollback处理事务 目录 MFC OBDC技术 . 3 概述. 3 CDatabase类操作数据源 . 3 CFieldExchange类处理数据交换 . 6 CRecordView类显示记录 . 6 CDBException类处理异常 . 7 使用MFC ODBC编程建立应用程序 . 7 MFC ODBC编程模型概述 . 7 通过App
3、Wizard建立数据库应用程序 . 8 使用CDatabase类方法打开数据源 . 8 使用CRecordste类打开记录集. 10 绑定记录集 . 12 参数化记录集和查询 . 13 遍历记录集合 . 15 书签定位和绝对定位 . 16 获取记录集的数据 . 18 添加记录 . 19 删除记录 . 21 修改记录 . 21 直接执行SQL语句 . 22 事务处理 . 23 使用多记录集 . 25 MFC OBDC技术 概述 MFC的ODBC类对较复杂的ODBC API进行了封装,提供了简化的调用接口。MFC的ODBC类主要包括以下5个类: CDatabase类:主要功能是建立与数据源的连接
4、CRecordset类:代表从数据源选择的一组记录 CRecordView类:提供了一个表单视图与某个记录集直接相连,利用对话框数据交替机制在记录集与表单视图的控件之间传输数据 CFieldExchange类:支持记录字段数据交换,即记录集字段数据成员与相应的数据库的表的字段之间的数据交换。 CDBException类:代表ODBC类产生的异常。 CDatabase类操作数据源 CDatabase类型的对象表示一个到数据源的连接,通过它可以操作数据源。 该类的成员函数如下表: 函数 CDatabase Close Open OpenEx BeginTrans BindParameters Ca
5、ncel CommitTrans 构造一个对象 关闭数据源连接 通过一个ODBC驱动程序创建到数据源的连接 通过一个ODBC驱动程序创建到数据源的连接 开始事务 允许在调用CDatabase:ExecuteSQL前绑定参数 取消异步操作或第二条线程中的过程 执行事务 说明 ExecuteSQL Rollback 执行SQL语句,不返回记录 回滚事务,数据源返回先前的状态 该类的属性属性如下表: 属性 CanTransact CanUpdate GetBookmarkPersistence GetConnect 说明 如果数据源支持事务,返回非零 如果CDatabase可以更新,返回非零 获得书
6、签对记录集对象的持久性 返回ODBC连接串 GetCursorCommitBehavior 获得提交事务对记录集对象的影响 GetCursorRollbackBehavior 获得回滚事务对记录集对象的影响 GetDatabaseName IsOpen SetLoginTimeout SetQueryTimeout 返回当前使用的数据库名 如果当前CDatabase对象连接到数据源,返回非零 设置数据源连接的超时数 设置查询操作的超时数 应用程序可使用多个CDatabase类型的对象。构造一个对象并调用Open成员函数打开一个连接。接着构造CRecordset类型的对象以操作连接的数据源,构造
7、时向记录集对象传递CDatabase类型的指针。完成使用后,用Close成员函数销毁CDatabase类型的对象。 一般情况下并不需要直接使用CDatabase类型的对象,因为CRecordset类型的对象可以实现大多数的功能、但是在进行事务处理时,CDatabase就起到关键作用。事务指的是将一系列对数据源的更新放在一起,同时提交或一个都不提交,为的是确保多用户对数据源同时操作时的数据正确性。 CRecordset类操作记录集 一个CRecordset类型的对象代表从数据源选择的一组记录的集合记录集,通过该类的方法实现对数据库中记录的各种操作。 该类常用的数据成员如下表: 成员 m_hstm
8、t m_nFields m_nParams m_pDatabase m_strFilter m_strSort 该类的构造方法如下表: 构造方法 Close CRecordset Open CRecordset类记录集属性如下表: 属性 CanAppend CanBookmark CanRestart CanScroll CanTransact CanUpdate 说明 包含记录集的ODBC陈述句柄,类型为HSTMT 包含记录集中字段数据成员的数量,类型为UNIT 包含记录集中参数数据成员的数量,类型为UNIT 包含一个CDatabase对象指针,通过它访问数据源 包含CString对象,定义
9、SQL中WHERE子句 包含CString对象,定义SQL中ORDER BY子句 说明 关闭记录集和与之相关的HSTMT 构造一个CRecordset对象 通过获得表或执行记录集所代表的查询来打开记录集 说明 如果新记录可以通过Addnew添加到记录集,返回非零 如果记录集支持书签,返回非零 如果Requery可以被调用来再次运行记录集查询,返回非零 如果可以在记录中回滚,返回非零 如果数据源支持事务,返回非零 如果记录集可以被更新,返回非零 GetODBCFieldCount 返回记录集中字段的数量 GetRecordCount GetSQL 返回记录集中记录的数量 获得SQL字符串 Get
10、Status GetTableName IsBOF IsDeleted IsEOF IsOpen 获得记录集的状态 获得记录集所属的表名 如果记录集定位在第一条记录之前,返回非零 如果记录集定位在一条删除的记录,返回非零 如果记录集定位在最后一条记录之后,返回非零 如果调用过Open函数,返回非零 CRecordset类更行操作如下表: 更新操作 AddNew CancelUpdate Delete Edit Update 说明 准备增加一条新纪录,调用Update之后完成添加 取消任何未完成的更新 从记录集中删除当前记录 准备对当前记录进行修改,调用Update后完成修改 通过将新记录或编辑
11、的数据存入数据源来完成AddNew或Edit操作 记录集有两种形式:snapshot和dynaset。 CFieldExchange类处理数据交换 CFieldExchange类支持数据库类所使用的记录集字段交换程式。如果使用自定义的数据类型写数据交换程式,会使用这个类。否则不会直接使用此类。RFX在记录集对象的字段数据成员与数据源中当前记录的相应字段之间交换数据。 CRecordView类显示记录 CRecordView对象用于在控件中显示数据库记录的视图。这种视图是一种直接连到一个CRecordView对象的格式视图,它从一个对话框模板创建资源,并将CRecordView对象的字段显示在对
12、话框模版的控件里。对象利用DDX和RFX机制,使窗体上的空间和记录集的字段值之间数据移动自动化,也就是说,用户不需要编写一行代码就可以完成简单的数据库记录查看程序。 CDBException类处理异常 由CException类派生,以3个继承的成员变量反映对数据库操作时的异常: m_nRetCode:以ODBC返回代码的形式表明造成一场的原因 m_strError:字符串,描述造成抛出异常的错误原因 m_strStateNativeOrigin:字符串,用以描述以ODBC错误代码表示的异常错误 使用MFC ODBC编程建立应用程序 MFC ODBC编程模型概述 使用MFC ODBC访问数据库比
13、直接使用ODBC API简单得多,编程步骤如下: 使用CDatabase打开数据源的连接,如果利用AppWizard生成一个ODBC数据库应用程序,则会自动完成操作。 使用ClassWizard向导加入由CRecordset类派生的用户记录集类,完成对数据库的绑定。 创建记录集类对象,如果利用AppWizard生成一个ODBC数据库应用程序,则会自动在文档类中创建。 使用记录及对象对数据库进行遍历、增加、删除、修改等操作。 使用CDatabase类的ExecuteSQL函数直接执行SQL命令。 使用CDatabase类的BeginTrans、CommitTrans和Rollback函数进行事务
14、处理 使用CDatabase类的Close函数关闭数据源连接。 通过AppWizard建立数据库应用程序 “New”-”MFC AppWizard(EXE)”-”OK”-”Single document(单文档)”-NEXT -”选择需要对什么样的数据库类型支持做出选择:None(不要任何数据支持,今后再添加很麻烦)、Header files only(该工程需要数据库支持,但不清楚细节时选择。工程会添加所要求的头文件和链接库,但必须自己在创建数据库类)、Database view without file support(表示包含数据库头文件和链接库,并创建记录集和记录视图,应用程序虽有文档
15、支持,但不支持串行化)、Database view with file support(表示包含数据库头文件、记录集和视图外,程序还支持串行化) -”Data Options 对话框:在”Datasource”中选择ODBC单选按钮,选择一个已经注册号的数据源(实例程序Sample中使用的数据源叫students)。在”Recordset type”(记录类型)中有三个选项:Snapshot(快照:它是当前表的一个静态视图。表打开之后,表中的所有数据马上被载入到应用程序中。其他用户或程序对表的修改只有在下次打开表时才会体现出来,看不到其他用户对表的”即时”修改,因此它是静态的。Snapshot
16、适用于用户查询信息(例如生成报表等)而不适用于数据编辑。)、Dynaset(动态集:这个选项创建指向所请球的每个记录的实际指针。只有屏幕需要显示记录时,才从数据库中提取数据。这种方式的好处是动态、即时的浏览到当前的记录。而其他用户也会即时看到你对记录所做出的修改。该选项适合于创建用户要发费很多时间来编辑数据的应用程序,并且,如果正在编写大型数据库应用程序,他也是最佳选择)、Table(表:表方法(仅使用DAO访问数据库时可用)把所做查询的内容放进一个临时表。这样做不但减小了从服务器下载的信息量,还意味着程序员有了更大的灵活性,因为可以直接操作临时表字段和记录。但缺点是你看不到别人的修改。使用D
17、AO且用户会执行同等数量的数据查询和数据编辑时,它是最佳选择。)” -”Selete Database Table(选择数据库表)” -”Finish”. 通过AppWizard创建了一个MFC ODBC数据库工程后,该工程与一般的应用程序工程有所不同:多了一个CRecordset的派生类,对应前面选择的数据库表;视图类的基类是CRecordView,负责显示数据;工具栏上多了遍历记录集的4个按钮。 使用CDatabase类方法打开数据源 通过CDatabase类的Open函数来打开数据源,该函数原型如下: virtual BOOL Open( LPCTSTR lpszDSN, /一个数据源名
18、,此数据源名是通过ODBC管理器注册的。如果DSN被设定在lpszConnect里,那么lpszDSN不应在被重新设定,lpszDSN应设为NULL。如果没有设定lpszConnect,而且又把lpszDSN设定为NULL,那么将出现一个对话框,让用户选择数据源。 BOOL bExclusive = FALSE, /默认为FALSE,表示以共享方式打开数据源。当前版本的类库不支持独占方式,如果设定为TRUE,将失败 BOOL bReadOnly = FALSE, /如果希望连接以只读方式打开,不想对数据源进行更新,那么设定为TRUE,所有依靠此连接打开的记录集全部继承此属性。默认值为FALSE
19、 LPCTSTR lpszConnect = _T(“ODBC;”), /连接串。连接串可能包含数据源名、数据源中用户的ID、密码和其他信息。整个连接串必须以”ODBC;”开头。”ODBC;”表示连接是一个ODBC数据源。 BOOL bUseCursorLib = TRUE /如果希望加载ODBC光标动态连接库,设定为TRUE ); 下面的例子表示如何打开数据源: /在文档类中加入 /CDatabase类对象 CDatabase m_pdatabase; /连接对象到一个数据源,ODBC连接对话框将是中隐藏 m_pdatabase.Open(_T(“students”), FALSE, FAL
20、SE, _T(“ODBC; UID = Admin”); /或者也可以显示ODBC对话框,请用户提供连接信息 m_pdatabase.Open(NULL); 像程序Sample一样,通过向导生成数据库工程,不用添加代码就能实现对数据源的打开。在CRecordset类中有一个名为GetDefaultConnect的虚函数值得注意,通过调用它可以返回默认的数据源连接来打开数据源。该函数如下: CString CSampleSet:GetDefaultConnect return _T(“ODBC; DSN = students”); 使用CRecordste类打开记录集 通过声明CRecordse
21、t记录集类的对象,再利用记录集类的Open函数可打开记录集,从而获取数据库中表的数据。也正是在调用Open函数后,记录集当中的成员变量得到数据源中表的字段值。Open函数的形式如下: virtual BOOL Open( ); 其中nOpenType为打开的类型,可取只有一下4种: CRecordset:dynaset:双向滚动的记录集,在记录集打开时,记录的顺序和成员就被确定了。其他用户对数据的修改在fetch操作之后才可访问,这也被叫做键集驱动的记录集。 CRecordset:dynamic:双向滚动的记录集,其他用户对数据的修改在fetch操作后才可访问。许多ODBC驱动程序都不支持此种
22、记录集。 CRecordset:ForwardOnly:只读记录集,只向前滚动。 第二个参数lpszSQL是一个CString的指针,指向以下内容之一: 一个NULL指针; 一个表名; 一个SQL查询语句 UNIT nOpenType = AFX_DB_USE_DEFAULT_TYPE, LPCTSTR lpszSQL = NULL, DWORD dwOptions = none 第三个参数dwOptions为一系列选项的组合,它的默认值为None。 可以像下面这样调用Open函数来打开记录集: CDatabase m_pdatabase; m_pdatabase.Open(_T(“stude
23、nts”), 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”), FA
24、LSE, 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”); 经过上面的操作
25、,记录集对象的字段变量就获得了数据库中特定表中指定字段的数据。 像程序Sample一样,在向导当中选择数据源和表名。在CRecordset类中有一个名为GetDefaultSQL的虚函数值得注意,通过调用它可以返回默认的SQL语句,用于形成记录集对象。该函数如下: CString CSampleSet:GetDefaultSQL return _T(“*students+”); 绑定记录集 通过向导创建工程后,程序的框架就生成出来。如果打开CRecordset的派生类CSampleSet,会发现里面已经有了5个变量: /Field/Param Data /AFX_FIELD(CSampleSe
26、t, CRecordset) long m_id; CString m_name; Long m_department; Long m_age; CString m_comment; /AFX_FIELD 这5个变量正好与要访问的表中的字段同名,并且变量的类型也与字段类型一致。这是MFC自动添加的变量,已绑定表中的字段。如果表中的字段是中文的,那么MFC会创建m_column1、m_column2等与之对应。RFX实现了这种绑定。 RFX,记录字段交换使记录集和隐藏于后台的数据源之间建立其对应的关系。用户只需要操作记录集,就可以实现对数据源的操作。MFC中提供了一组RFX调用函数,利用这些函数
27、,就可以使记录集中的变量与数据源中的字段对应起来。使记录集和数据之间进行数据交换成为可能,并且,这种交换是双向的。 可以在CSampleSet类的DoFieldExchange函数中看到一组组的RFX函数调用,正是通过调用它们,使CSampleSet记录集中的变量与students表中的字段对应起来。 void CSampleSet: DoFieldExchange(CFieldExchange *pFx) /AFX_FIELD_MAP(CSampleSet) pFX-SetFieldType(CFieldExchange:outputColumn); RFX_Long(pFX, _T(“*i
28、d+”), 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个参数。第一个参数为一个指向CFieldExchange类对象的指针,第二个参数为数据源中的一个字段名称,第三个参数是与字段相对应的记录集中的变量名。常用的RFX函数如下表: 函数 RFX_Bool
29、 RFX_Byte RFX_Binary RFX_Double RFX_Sing RFX_Int RFX_Long RFX_LongBinary RFX_Text RFX_Date 数据类型 BOOL BYTE CByteArray Double Float Int Long CLongBinary CString CTime 参数化记录集和查询 CRecordset类对象中有两个成员变量,一个为m_strFilter(过滤字符串,负责对记录集进行过滤,返回过滤后的记录),另一个为m_strSort(排序字符串,对记录集进行排序)。 m_strFilter存放着SQL语句中WHERE子句的条件
30、字符串,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
31、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”
32、; m_pSet-Requery; 参变量的值按绑定的顺序替换查询字符串中的“?”适配符。代码中的m_pSet是CRecordView类的一个记录集指针,指向当前文档类中的记录集变量。它是在CRecordView类的OnInitialUpdate中被赋予文档类下记录集对象的指针的。下面是程序中CSampleView类的OnInitialUpdate函数体: void CSampleView:OnInitialUpdate /为m_pSet赋予文档类下的记录集对象的指针 m_pSet = &GetDocument-m_sampleSet; CRecordView:OnInitialUpdate;
33、 ResizeParentToFit; 遍历记录集合 CRecordset类中有一组函数负责记录集指针的移动,例如使用记录集指针下移一个记录、使用记录集指针上移一个记录等。 1、 2、 3、 4、 5、 MoveFirst函数:使指针移动到第一条记录 MoveLast函数:使指针移动到最后一条记录 MoveNext函数:使指针移动到下一条记录 MovePrev函数:使指针移动到前一条记录 IsBOF函数:当指针移动到第一条记录前面或者表中没有记录的时候返回真 6、 IsEOF函数:当指针移动到最后一条记录后面的时候返回真 知道上面的这些函数的意义后,下面来看如何遍历记录集。下面的代码中m_pS
34、et是一个记录集指针,m_list为一个列表框控件的变量: if(!m_pSet-IsOpen) m_pSet-Open; /用IsOpen函数检测记录集是否打开 m_pSet-MoveFirst; while(!m_pSet-IsEOF) m_list.AddString(m_pSet-m_name); m_pSet-MoveNext; m_pSet-MoveFirst; /遍历完成后,使记录集指针指向第一条记录 书签定位和绝对定位 当在记录集中浏览的时候,可能想返回记录集中特定的一条记录,CRecordset提供了两种方法可以指定记录集到特定的位置。 1、 书签定位 可以在记录集中的某一条
35、记录增加一个书签。在记录集浏览时由于用户的增删操作使记录的绝对位置发生改变,所以以来绝对位置是不可靠的。因此需要使用书签定位来为所想要的记录定位。CRecordset类中提供的书签定位的方法是GetBookmark和SetBookmark两个函数,他们的原型如下: void GetBookmark(CDBVariant& varBookmark); /参数为CDBVariant的对象 void SetBookmark(const CDBVariant& varBookmark); 这里只需直接使用CDBVariant的对象即可。 /创建CDBVariant对象 CDBVariant bookm
36、ark; /rs是CRecordset类或CRecordset类派生类的对象 rs.GetBookmark(bookmark); /一系列移动到其他记录的代码 rs.MoveNext; rs.MoveNext; rs.SetBookmark(bookmark); GetBookmark函数将当前的记录存入一个CDBVariant的对象中,经过一系列的纪录移动之后,在调用SetBookmark,并且用刚才记录“书签”的CDBVariant对象bookmark作参数来使用当前记录集重新指向“书签”的位置。 是否支持书签定位取决于ODBC驱动程序和记录集类型。可以通过调用CRecordset:Can
37、Bookmark来确定是否支持书签定位。如果想支持书签定位,还需要在记录集的Open函数的dwOptions参数位置中加入CRecordset:useBookmarks参数。注意forward-only recordsets(只向前)类型的记录集也不支持书签定位。还有一点,就是在某些记录集操作之后,也应该及时检查前面所设置的“书签”是否还可以继续使用。例如,对一个记录及进行了Requery操作之后,书签就可能不再有效了。所以,在调用SetBookmark函数之前,应该先调用CDatabase:GetBookmarkPersistence函数来核对是否可以安全的调用SetBookmark函数。下
38、面是该函数原型:DWORD GetBookmarkPersistence const; 这个函数的返回值为bitmask,这是一个DWORD类型的返回值。该值可以是下表中的多个bitmask值的组合。 bitmask值 SQL_BP_CLOSE SQL_BP_DELETE SQL_BP_DROP SQL_BP_SCROLL SQL_BP_TRANSACTION SQL_BP_UPDATE SQL_BP_OTHER_HSTMT 2、 绝对定位 书签的有效性 Requery操作后,书签有效 对某行执行delete操作后,书签对此行依然有效 一次Close操作后,书签有效 任何Move操作之后,书签
39、都有效 一次事务被提交或回滚后,书签有效 对某行执行Update操作后,书签对此行有效 与某记录集对象相关的书签对另一记录集也有效 相对于书签定位,绝对定位就好像记住某本书的某一个固定页码一样。绝对定位就是通过原始的记录位置来设置当前记录,比如可以设置记录集中第8条记录为当前记录。如想使用绝对定位来改变当前的记录集位置,可以调用CRecordset:SetAbsolutePosition函数。其原型为: void SetAbsolutePosition(long nRows); nRows表示记录集中的一个绝对位置。调用该函数会把记录集指针定位到nRows参数所指行号的记录上。 下面的代码表示
40、把记录集定位到第12条记录的位置上: long row; row = 12; rs.SetAbsolutePositon(row); 对于ODBC的记录集来说,绝对位置1指的是记录集当中的第一条记录,绝对位置如果是0则代表的是BOF位置。 类型的记录集不支持SetAbsolutePosition方法。此外,记录的绝对位置存在潜在的不可靠性。如果用户删除了某一条记录,那么后续记录的位置都发上变化,并且记录集也可能被再次重新创建,不能确保某条记录在创建的记录集中有与原来相同位置,因此建议使用书签定位。 *) 获取记录集的数据 通过在对话框上添加控件,并且为控件绑定变量来达到数据交换并显示的目的,与控件绑定的变量正是记录集中的那些字段变量