现在的位置: 首页 > 数据库 > 正文

利用支持ODBC的CRecordset类实现对数据库的操作

2018年11月01日 数据库 ⁄ 共 7830字 ⁄ 字号 评论关闭

 

1.MFC中的ODBC类

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

(2)CRecordset类:封装大部分操纵数据库的方法,包括浏览、修改记录、控制游标移动,排序等操作。CRecordset类是MFCODBC类中最重要、功能最强大的一个类。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.记录集的建立和关闭

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

(2)virtual CString GetDefaultConnect();
该函数返回缺省的连接字符串。Open函数在必要的时侯会调用该函数获取连接字符串以建立与数据源的连接。一般需要在CRecordset派生类中覆盖该函数并在新版的函数中提供连接字符串。
例如:CSetdataCRecordset类的派生类
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是一个SQLSELECT语句,或是一个表名。函数用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_strFilterm_strSort用来设置对记录的过滤和排序。在调用OpenRequery前,如果在这两个数据成员中指定了过滤或排序,那么OpenRequery将按这两个数据成员指定的过滤和排序来查询数据源。
   成员m_strFilter用于指定过滤器。m_strFilter实际上包含了SQLWHERE子句的内容,但它不含WHERE关键字。使用m_strFilter的一个例子为:
m_pSet->m_strFilter=“CourseID=‘MATH101’”; //只选择CourseIDMATH101的记录
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_strFilterm_strSort的内容放入SELECT语句的WHEREORDER
BY子句中.如果在OpenlpszSQL参数中已包括了WHEREORDER BY子句,那么m_strFilterm_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_NEXTSQL_FETCH_PRIORSQL_FETCH_FIRSTSQL_FETCH_LASTSQL_FETCH_ABSOLUTE,可以完成上面五个函数的功能.若wFetchTypeSQL_FETCH_RELATIVE,那么将相对当前记录移动,若nRows为正数,则向前移动,若nRows为负数,则向后移动。 

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

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

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)可退出添加模式,同时该函数会从缓冲区中恢复域数据成员.

设置域数据成员.

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

如果记录集是快照,那么在添加一个新的记录后,需要调用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则表明可以添加记录.

 

抱歉!评论已关闭.