ODBC中怎么利用CRecordset类对数据库进行操作

84次阅读
没有评论

共计 7824 个字符,预计需要花费 20 分钟才能阅读完成。

ODBC 中怎么利用 CRecordset 类对数据库进行操作,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

 

1.MFC 中的 ODBC 类

主要有 CDatabase、CRecordset、CRecordview、CDBException、CFieldExchange。这些类封装了 ODBC
SDK 函数,可以很方便的操作支持 ODBC 的数据库。
(1)CDatabase 类:封装应用程序与需要访问的数据库之间的连接,控制事务的提交和执行 SQL 语句的方法。

(2)CRecordset 类:封装大部分操纵数据库的方法,包括浏览、修改记录、控制游标移动,排序等操作。CRecordset 类是 MFC 的 ODBC 类中最重要、功能最强大的一个类。CRecordset 类对象提供了从数据源中提取出的记录集。在多任务操作系统或网络环境中,多个用户可以共享同一个数据源。共享数据的一个主要问题是如何协调各个用户对数据源的修改。CRecordset 提供了几种不同的方法来处理这个问题,这将由程序采用哪种类型的记录集来决定。

CRecordset 对象通常用于两种形式:动态行集 (dynasets) 和快照集(snapshots)。动态行集能与其他用户所做的更改保持同步,快照集则是数据的一个静态视图,当别的用户改变了记录时(包括修改、添加和删除),快照中的记录不受影响,也就是说,快照不反映别的用户对数据源记录的改变.直到调用了 CRecordset::Requery 重新查询后,快照才会反映变化。每一种形式在记录集被打开时都提供一组记录,所不同的是,当在一个动态行集里滚动到一条记录时,由其他用户或是应用程序中其他记录集对该记录所做的更改会相应地显示出来,例如在火车联网销售系统中,应该实时的显示共享数据的变化。

(3)CRecordView 类:提供与 CRecordset 对象相连接的视图,可以建立视图中的控件与数据库数据的对应,同时支持游标,修改记录等操作。

(4)CDBException 类:提供对数据库操作的异常处理。

(5)CFieldExchange 类:提供用户变量与数据库字段之间的数据交换。

2. 域数据成员与数据交换

CRecordset 类代表一个记录集。用户一般用 ClassWizard 创建一个 CRecordset 的派生类。ClassWizard 可以为派生的记录集类创建一批数据成员,这些数据成员与记录的各字段相对应,被称为字段数据成员或域数据成员。域数据成员与表中的字段名字类似,且类型匹配。
例如:这是一个 CRecordset 类的派生类的定义
class CSetdata : public CRecordset
{
public:
 CSetdata(CDatabase* pDatabase = NULL);
 DECLARE_DYNAMIC(CSetdata)

// Field/Param Data
 //{{AFX_FIELD(CSetdata, CRecordset)
 // 定义域数据成员变量,域数据成员用来与记录集对应字段进行数据交换,起到一个缓冲区或中间桥梁的作用。也就是说,我们在处理记录集时,实际上是对域数据成员进行操作,而不是直接对数据库中的数据操作。
 CString m_number; 
 CString m_name;
 CString m_sex;
 long m_age;
 //}}AFX_FIELD
 CString number;

// Overrides
 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CSetdata)
 public:
 virtual CString GetDefaultConnect();  // Default connection string
 virtual CString GetDefaultSQL();  // Default SQL for Recordset
 virtual void DoFieldExchange(CFieldExchange* pFX);  // RFX support
 //}}AFX_VIRTUAL

// Implementation
#ifdef _DEBUG
 virtual void AssertValid() const;
 virtual void Dump(CDumpContext dc) const;
#endif
};

#endif 

域数据成员用来保存某条记录的各个字段,它们是程序与记录之间的缓冲区。域数据成员代表当前记录,当在记录集中滚动到某一记录时,框架自动地把记录的各个字段拷贝到记录集对象的域数据成员中。当用户要修改当前记录或增加新记录时,程序先将各字段的新值放入域数据成员中,然后调用相应的 CRecordset 成员函数把域数据成员设置到数据源中。

不难看出,在记录集与数据源之间有一个数据交换问题.CRecordset 类使用 记录域交换 (Record Field Exchange,缩写为 RFX)机制自动地在域数据成员和数据源之间交换数据。RFX 机制与对话数据交换 (DDX) 类似。CRecordset 的成员函数 DoFieldExchange 负责数据交换任务,在该函数中调用了一系列 RFX 函数。当用户用 ClassWizard 加入域数据成员时,ClassWizard 会自动在 DoFieldExchange 中建立 RFX。
典型的 DoFieldExchange 函数

void CSetdata::DoFieldExchange(CFieldExchange* pFX)
{
 //{{AFX_FIELD_MAP(CSetdata)
 pFX- SetFieldType(CFieldExchange::outputColumn);
 RFX_Text(pFX, _T( [number] ), m_number);  // 实现了域数据成员和数据源之间的数据交换
 RFX_Text(pFX, _T( [name] ), m_name);
 RFX_Text(pFX, _T( [sex] ), m_sex);
 RFX_Long(pFX, _T( [age] ), m_age);
 //}}AFX_FIELD_MAP
 pFX- SetFieldType(CFieldExchange::param);
 RFX_Text(pFX,_T( [] ),number);
}

3. 记录集的建立和关闭

使用 MFC 的 ODBC 类操作数据库首先要建立一个 CRecordset 的派生类来关联对应的数据表,然后调用 Open()函数来查询数据源中的数据并建立记录集。此外,在 Open 函数中,可能还会调用 GetDefaultConnect 和 GetDefaultSQL 函数。
(1)首先在派生类的构造函数中会有一个参数指向一个 CDatabase 对象,用来获取数据源。函数如下:
CRecordset(CDatabase* pDatabase = NULL);
如果 pDatabase 为 NULL,则会在 Open 函数中自动构建一个 CDatabase 对象。如果 CDatabase 对象还未与数据源连接,那么在 Open 函数中会建立连接,连接字符串由成员函数 GetDefaultConnect 提供。

(2)virtual CString GetDefaultConnect();
该函数返回缺省的连接字符串。Open 函数在必要的时侯会调用该函数获取连接字符串以建立与数据源的连接。一般需要在 CRecordset 派生类中覆盖该函数并在新版的函数中提供连接字符串。
例如:CSetdata 为 CRecordset 类的派生类
CString CSetdata::GetDefaultConnect()
{
 return _T(ODBC;DSN=my data
}
这个函数获取了数据库连接字符串,包括连接方式以及数据源的名称。

(3)virtual CString GetDefaultSQL();
Open 函数在必要时会调用该函数返回缺省的 SQL 语句或表名以查询数据源中的记录。一般需要在 CRecordset 派生类中覆盖该函数并在新版的函数中提供 SQL 语句或表名。
例如:
CString CSetdata::GetDefaultSQL()
{
 return _T([bingli]
}
这个类返回的是数据源中数据表 bingli 的名称。

(4)virtual BOOL Open(UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE, LPCTSTR lpszSQL = NULL, DWORD dwOptions = none);
throw(CDBException, CMemoryException);
该函数使用指定的 SQL 语句查询数据源中的记录并按指定的类型和选项建立记录集。参数 nOpenType 说明了记录集的类型,如下表所示。如果要求的类型驱动程序不支持,则函数将产生一个异常。参数 lpszSQL 是一个 SQL 的 SELECT 语句,或是一个表名。函数用 lpszSQL 来进行查询,如果该参数为 NULL,则函数会调用 GetDefaultSQL 获取缺省的 SQL 语句。参数 dwOptions 可以是一些选项的组合,常用的选项在下表中列出。若创建成功则函数返回 TRUE,若函数调用了 CDatabase::Open 且返回 FALSE,则函数返回 FALSE.

记录集的类型
类型  
含义  
AFX_DB_USE_DEFAULT_TYPE  使用缺省值. 
CRecordset::dynaset  可双向滚动的动态集. 
CRecordset::snapshot  可双向滚动的快照. 
CRecordset::dynamic  提供比动态集更好的动态特性,大部分 ODBC 驱动程序不支持这种记录集. 
CRecordset::forwardOnly  只能前向滚动的只读记录集. 
 
创建记录集时的常用选项  
选项  
含义  
CRecordset::none  无选项(缺省). 
CRecordset::appendOnly  不允许修改和删除记录,但可以添加记录. 
CRecordset::readOnly  记录集是只读的. 
CRecordset::skipDeletedRecords 有些数据库 (如 FoxPro) 在删除记录时并不真删除,而是做个删除标记,在滚动时将跳过这些被删除的记录。
 
如果所有的参数都为空,例如:
CSetdata *pset = new CSetdata(); 
pset- Open();
那么 Open()函数将会调用 GetDefaultSQL()函数获取指定数据表的数据并建立数据集。实际上,如果只提供表名,CRecordset 类会构造一个 SELECT 语句来查询数据源。例如:SELECT
(能提供的字段名) FROM bingli(即 GetDefaultSQL()函数返回的表名)。

(5) 建立记录集后,用户可以随时调用 Requery 成员函数来重新查询和建立记录集。Requery 有两个重要用途:

使记录集能反映用户对数据源的改变

按照新的过滤或排序方法查询记录并重新建立记录集.

在调用 Requery 之前,可调用 CanRestart 来判断记录集是否支持 Requery 操作。要记住 Requery 只能在成功调用 Open 后调用,所以程序应调用 IsOpen 来判断记录集是否已建立.函数的声明为

virtual BOOL Requery();
throw(CDBException, CMemoryException);
返回 TRUE 表明记录集建立成功,否则返回 FALSE。若函数内部出错则产生异常.

BOOL CanRestart() const; // 若支持 Requery 则返回 TRUE
BOOL IsOpen() const; // 若记录集已建立则返回 TRUE

CRecordset 类有两个公共数据成员 m_strFilter 和 m_strSort 用来设置对记录的过滤和排序。在调用 Open 或 Requery 前,如果在这两个数据成员中指定了过滤或排序,那么 Open 和 Requery 将按这两个数据成员指定的过滤和排序来查询数据源。
  成员 m_strFilter 用于指定过滤器。m_strFilter 实际上包含了 SQL 的 WHERE 子句的内容,但它不含 WHERE 关键字。使用 m_strFilter 的一个例子为:
m_pSet- m_strFilter=“CourseID=‘MATH101’”; // 只选择 CourseID 为 MATH101 的记录
if(m_pSet- Open(CRecordset::snapshot,“Section”))

. . . . . .

成员 m_strSort 用于指定排序.m_strSort 实际上包含了 ORDER BY 子句的内容,但它不含 ORDER BY 关键字.m_strSort 的一个例子为

m_pSet- m_strSort=“CourseID DESC”; // 按 CourseID 的降序排列记录

m_pSet- Open();

. . . . . .

事实上,Open 函数在构造 SELECT 语句时,会把 m_strFilter 和 m_strSort 的内容放入 SELECT 语句的 WHERE 和 ORDER
BY 子句中.如果在 Open 的 lpszSQL 参数中已包括了 WHERE 和 ORDER BY 子句,那么 m_strFilter 和 m_strSort 必需为空.

调用无参数成员函数 Close 可以关闭记录集.在调用了 Close 函数后,程序可以再次调用 Open 建立新的记录集.CRecordset 的析构函数会调用 Close 函数,所以当删除 CRecordset 对象时记录集也随之关闭。

4. 滚动记录

CRecordset 提供了几个成员函数用来在记录集中滚动,如下所示.当用这些函数滚动到一个新记录时,框架会自动地把新记录的内容拷贝到域数据成员中。

void MoveNext();  // 前进一个记录
void MovePrev();  // 后退一个记录
void MoveFirst(); // 滚动到记录集中的第一个记录
void MoveLast();  // 滚动到记录集中的最后一个记录
void SetAbsolutePosition(long nRows); 
该函数用于滚动到由参数 nRows 指定的绝对位置处.若 nRows 为负数,则从后往前滚动.例如,当 nRows 为 - 1 时,函数就滚动到记录集的末尾。注意,该函数不会跳过被删除的记录。

virtual void Move(long nRows, WORD wFetchType = SQL_FETCH_RELATIVE);
该函数功能强大.通过将 wFetchType 参数指定为 SQL_FETCH_NEXT、SQL_FETCH_PRIOR、SQL_FETCH_FIRST、SQL_FETCH_LAST 和 SQL_FETCH_ABSOLUTE,可以完成上面五个函数的功能.若 wFetchType 为 SQL_FETCH_RELATIVE,那么将相对当前记录移动,若 nRows 为正数,则向前移动,若 nRows 为负数,则向后移动。

  如果在建立记录集时选择了 CRecordset::skipDeletedRecords 选项,那么除了 SetAbsolutePosition 外,在滚动记录时将跳过被删除的记录,这一点对象 FoxPro 这样的数据库十分重要。

如果记录集是空的,那么调用上述函数将产生异常。另外,必须保证滚动没有超出记录集的边界。调用 IsEOF 和 IsBOF 可以进行这方面的检测。

BOOL IsEOF() const; // 如果记录集为空或滚动过了最后一个记录,那么函数返回 TRUE,否则返回 FALSE。 
BOOL IsBOF() const; // 如果记录集为空或滚动过了第一个记录,那么函数返回 TRUE,否则返回 FALSE。

下面是一个使用 IsEOF 的例子:
while(!m_pSet- IsEOF())
m_pSet- MoveNext();

调用 GetRecordCound 可获得记录集中的记录总数,该函数的声明为:
long GetRecordCount() const; 
要注意这个函数返回的实际上是用户在记录集中滚动的最远距离.要想真正返回记录总数,只有调用 MoveNext 移动到记录集的末尾(MoveLast 不行)。

5. 修改、添加和删除记录

要修改当前记录,应该按下列步骤进行:

调用 Edit 成员函数.调用该函数后就进入了编辑模式,程序可以修改域数据成员.注意不要在一个空的记录集中调用 Edit,否则会产生异常.

Edit 函数会把当前域数据成员的内容保存在一个缓冲区中,这样做有两个目的,一是可以与域数据成员作比较以判断哪些字段被改变了,二是在必要的时侯可以恢复域数据成员原来的值.若再次调用 Edit,则将从缓冲区中恢复域数据成员,调用后程序仍处于编辑模式.调用 Move(AFX_MOVE_REFRESH)或 Move(0)可退出编辑模式(AFX_MOVE_REFRESH 的值为 0),同时该函数会从缓冲区中恢复域数据成员.

设置域数据成员的新值.

调用 Update 完成编辑.Update 把变化后的记录写入数据源并结束编辑模式.

要向记录集中添加新的记录,应该按下列步骤进行:

调用 AddNew 成员函数.调用该函数后就进入了添加模式,该函数把所有的域数据成员都设置成 NULL(注意,在数据库术语中,NULL 是指没有值,这与 C ++ 的 NULL 是不同的).与 Edit 一样,AddNew 会把当前域数据成员的内容保存在一个缓冲区中,在必要的时侯,程序可以再次调用 AddNew 取消添加操作并恢复域数据成员原来的值,调用后程序仍处于添加模式.调用 Move(AFX_MOVE_REFRESH)可退出添加模式,同时该函数会从缓冲区中恢复域数据成员.

设置域数据成员.

调用 Update.Update 把域数据成员中的内容作为新记录写入数据源,从而结束了添加.

如果记录集是快照,那么在添加一个新的记录后,需要调用 Requery 重新查询,因为快照无法反映添加操作.

要删除记录集的当前记录,应按下面两步进行:

调用 Delete 成员函数.该函数会同时给记录集和数据源中当前记录加上删除标记.注意不要在一个空记录集中调用 Delete,否则会产生一个异常.

滚动到另一个记录上以跳过删除记录.

上面提到的函数声明为:

virtual void Edit();throw( CDBException, CMemoryException);

virtual void AddNew();throw( CDBException);

virtual void Delete();throw( CDBException);

virtual BOOL Update();throw( CDBException); 
若更新失败则函数返回 FALSE,且会产生一个异常.

在对记录集进行更改以前,程序也许要调用下列函数来判断记录集是否是可以更改的,因为如果在不能更改的记录集中进行修改、添加或删除将导致异常的产生.

BOOL CanUpdate() const; // 返回 TRUE 表明记录是可以修改、添加和删除的.

BOOL CanAppend() const; // 返回 TRUE 则表明可以添加记录.

关于 ODBC 中怎么利用 CRecordset 类对数据库进行操作问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注丸趣 TV 行业资讯频道了解更多相关知识。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-03发表,共计7824字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)