Visual C++数据库编程(ODBC)


ODBC基本概念

 

ODBC(Open Database Connectivity,开放数据库互连)是微软公司开放服务结构(WOSAWindows Open Services Architecture)中有关数据库的一个组成部分,它建立了一组规范,并提供了一组对数据库访问的标准API(应用程序编程接口)。这些API利用SQL来完成其大部分任务。ODBC本身也提供了对SQL语言的支持,用户可以直接将SQL语句送给ODBC

  一个基于ODBC的应用程序对数据库的操作不依赖任何DBMS,不直接与DBMS打交道,所有的数据库操作由对应的DBMSODBC驱动程序完成。也就是说,不论是FoxProAccess还是Oracle数据库,均可用ODBC API进行访问。由此可见,ODBC的最大优点是能以统一的方式处理所有的数据库。

  一个完整的ODBC由下列几个部件组成:

  应用程序(Application)

  ODBC管理器(Administrator)。该程序位于Windows 95控制面板(Control Panel)32ODBC内,其主要任务是管理安装的ODBC驱动程序和管理数据源。

  驱动程序管理器(Driver Manager)。驱动程序管理器包含在ODBC32.DLL中,对用户是透明的。其任务是管理ODBC驱动程序,是ODBC中最重要的部件。

  ODBC API

  ODBC 驱动程序。是一些DLL,提供了ODBC和数据库之间的接口。

  数据源。数据源包含了数据库位置和数据库类型等信息,实际上是一种数据连接的抽象。

各部件之间的关系如图下图所示:

应用程序要访问一个数据库,首先必须用ODBC管理器注册一个数据源,管理器根据数据源提供的数据库位置、数据库类型及ODBC驱动程序等信息,建立起ODBC与具体数据库的联系。这样,只要应用程序将数据源名提供给ODBCODBC就能建立起与相应数据库的连接。

  在ODBC中,ODBC API不能直接访问数据库,必须通过驱动程序管理器与数据库交换信息。驱动程序管理器负责将应用程序对ODBC API的调用传递给正确的驱动程序,而驱动程序在执行完相应的操作后,将结果通过驱动程序管理器返回给应用程序。

  在访问ODBC数据源时需要ODBC驱动程序的支持。用Visual C++ 5.0安装程序可以安装SQL Server Access Paradox dBase FoxPro Excel Oracle Microsoft Text等驱动程序.在缺省情况下,VC5.0只会安装SQL Server Access FoxProdBase的驱动程序.如果用户需要安装别的驱动程序,则需要重新运行VC 5.0的安装程序并选择所需的驱动程序。 

 

 

 

ADO对象访问模型

本节简要介绍了微软的活动数据对象(ADO)模型,结合实例阐述了在Visual C++环境下使用ADO操纵数据库的基本步骤,分析ADO的特点及与开放式数据库连接(ODBC)的差异与应用前景。

关键词: 活动数据对象 数据库 Visual C++

ADO是微软整个COM战略体系中的一个组成部分

  活动数据对象(ADO)是一组由微软提供的COM组件。 ADO建立在微软所提倡的COM体系结构之上,它的所有接口都是自动化接口,因此在C++、VisualBasic、Delphi等支持COM的开发语言中通过接口都可以访问到ADO。ADO通过使用OLE DB这一新技术实现了以相同方式可以访问关系数据库、文本文件、非关系数据库、索引服务器和活跃目录服务等的数据,扩大了应用程序中可使用的数据源范围,从而成为微软整个COM战略体系中访问数据源组件的首选,是ODBC的替代产品。 

 

ADO对象模型组成
  与微软的其它数据访问模型DAO和RDO相比,ADO对象模型非常精炼,仅由三个主要对象Connection、Command、Recordset和几个辅助对象组成,其相互关系如图所示。Connection对象提供OLE DB数据源和对话对象之间的关联,它通过用户名称和口令来处理用户身份的鉴别,并提供事务处理的支持;它还提供执行方法,从而简化数据源的连接和数据检索的进程。Command对象封装了数据源可以解释的命令,该命令可以是SQL命令、存储过程或底层数据源可以理解的任何内容。Record set用于表示从数据源中返回的表格数据,它封装了记录集合的导航、记录更新、记录删除和新记录的添加等方法,还提供了批量更新记录的能力。其它辅助对象则分别提供封装ADO错误、封装命令参数和封装记录集合的列。 

ADO的特点分析

  (1)由于封装了许多底层工作,使用ADO与使用ODBC几乎是一样方便。

  (2) ADO不仅具有ODBC的主要功能,而且ADO适用的数据源的范围要大的多。

  (3)在定义ADO记录集变量和数据库表字段绑定类时,要求记录集的字段变量、状态变量与数据库表字段的个数、顺序必须相同。这一点比在FMC中使用ODBC要复杂一些。但在数据库字段与ADO记录集字段变量绑定的宏中,ADO 提供的数据类型要远多于FMC中的RFX(如日期时间类型,在ODBC中只能转换为Cstring类型)。

  (4)ADO允许同一Connection实例下有多个Record set实例。

  (5)ADO允许进行批更新(使用的Update Batch方法),这样将大大减轻网络负担,提高数据库处理效率。

 4 ADO在Visual C++中的使用

  利用微软在Micrsoft Studio 6中提供的ADO2,可以在Visual C++中使用ADO接口操纵SQL SERVER数据库。在编译型高级语言中使用ADO,比起在一些脚本语言(如Visual Basic Scropt和JavaScript)中使用ADO要困难一些。

  以下给出一个Visual C++下使用ADO的Connection对象及其Record set对象的基本步骤:

  (1) 使用import指令引入ADO2组件

  例:#import "C:ADOmsado15.dll" no_namespace rename("EOF", "EndOfFile") 

  (2) 定义CADORecordBinding 的派生类,用于程序与数据库表字段的交互,该类的定义可参见icrsint.h。

  例:

class CIntlive public CADORecordBinding 

public:
DBTIMESTAMP m_datetime; //定义ADO记录集字段变量(与数据库表字段相对应)
long m_key;
long m_value;
long m_quality;
WORD m_stsdatetime; //定义ADO记录集状态变量
WORD m_stskey;
WORD m_stsvalue;
WORD m_stsquality;
BEGIN_ADO_BINDING(CIntlive) //将数据库字段与ADO记录集字段变量绑定
ADO_VARIABLE_LENGTH_ENTRY2(1,adDBTimeStamp,m_datetime,sizeof(m_datetime),m_stsdatetime,true)
ADO_NUMERIC_ENTRY(2,adInteger,m_key,10,0,m_stskey,true)
ADO_NUMERIC_ENTRY(3,adInteger,m_value,10,0,m_stsvalue,true)
ADO_NUMERIC_ENTRY(4,adInteger,m_quality,10,0,m_stsquality,true)
END_ADO_BINDING()
};

  (3) 调用CoInitialize初始化COM ::CoInitialize(NULL); 

  (4) 声明ADO的Connection对象指针和Recordset对象指针并初始化。(类型名在 msado15.dll中已定义)

  例:

_ConnectionPtr pConnection1 NULL;
_RecordsetPtr rstADO1 NULL; 

  (5) 定义CADORecordBinding派生类的实例及其Bind接口指针。

  例:

CIntlive m_intdata; 
IADORecordBinding *rstADOBind1 NULL; 

  (6) 产生Connection对象实例和Record set对象实例。

  例:

pConnection1.CreateInstance(_uuidof(Connection)); 
rstADO1.CreateInstance(__uuidof(Recordset)) 

  (7) 连接到数据库并打开Record set对象,其中open函数的参数的使用方法可参见微软MSDN中ADO 相应对象参数的Basic描述。

  例:

PConnection1->Open("driver={SQL server};server=servera;uid=sa;pwd=;database=pubs","","",NULL);
rstADO1->Open("data", _variant_t((IDispatch *)pConnection1,true), 
adOpenKeyset,adLockBatchOptimistic, adCmdTable); 

  (8) 将CADORecordBinding派生类的实例联编到Record set对象的Bind接口。

  例:

RstADOBind1->BindToRecordset(&m_intdata); 

  (9) 对Record set对象实例进行操作。操作方法可参见微软MSDN中ADO Record set对象相应方法的Basic描述。

   例:

rstADO1->Move Next(); //移动游标到下一条记录
rstADO1->Update(_variant_t("quality"),_variant_t("3"))); //修改记录的quality字段的值为3
rstADO1->Update Batch(adAffectAll)); //将在Record set对象上的所有更新一次送入数据库

  (10) 关闭Record set对象并释放Bind接口。

  例:

RstADO1->Close(); 
RstADOBind2->Release(); 

  (11) 关闭连接 pConnection1->Close(); 

  (12) 调用CoUnitialize释放COM资源 ::CoUninitialize(); 

结论

  作为ODBC的替代产品,ADO确实有其过人之处。由于ADO数据源几乎覆盖了目前常见的数据源类型,对于ODBC所不支持的数据源,ADO无疑是唯一的选择。而ADO的批更新功能,更是网络环境下大数据量更新应用的重要因素。由于ADO缺乏大量的第三方厂商的支持,使得ADO目前远不如ODBC普及,但其面向对象的特性将使ADO具有比较广阔的发展前景。

 

ODBC编程实例

 

Microsoft Developer Studio为大多数标准的数据库格式提供了32位ODBC驱动器。这些标准数据格式包括有:SQL Server、Access、Paradox、dBase、FoxPro、Excel、Oracle以及Microsoft Text。如果用户希望使用其他数据格式,则需要安装相应的ODBC驱动器及DBMS。

  用户使用自己的DBMS数据库管理功能生成新的数据库模式后,就可以使用ODBC来登录数据源。对用户的应用程序来说,只要安装有驱动程序,就能注册很多不同的数据库。登录数据库的具体操作参见有关ODBC的联机帮助。 

  一、MFC提供的ODBC数据库类

  Visual C++的MFC基类库定义了几个数据库类。在利用ODBC编程时,经常要使用到 CDatabase(数据库类)、CRecordSet(记录集类)和CRecordView(可视记录集类)。

  CDatabase类对象提供了对数据源的连接,通过它可以对数据源进行操作。

  CRecordSet类对象提供了从数据源中提取出的记录集。CRecordSet对象通常用于两种形式:动态行集(dynasets)和快照集(snapshots)。动态行集能与其他用户所做的更改保持同步,快照集则是数据的一个静态视图。每种形式在记录集被打开时都提供一组记录,所不同的是,当在一个动态行集里滚动到一条记录时,由其他用户或应用程序中的其他记录集对该记录所做的更改会相应地显示出来。

  CRecordView类对象能以控件的形式显示数据库记录,这个视图是直接连到一个CRecordSet对象的表视图。

  二、应用ODBC编程

  应用Visual C++的AppWizard可以自动生成一个ODBC应用程序框架,步骤是:打开File菜单的New选项,选取Projects,填入工程名,选择MFC AppWizard (exe),然后按AppWizard的提示进行操作。

  当AppWizard询问是否包含数据库支持时,如果想读写数据库,那么选定Database view with file support;如果想访问数据库的信息而不想写回所做的改变,那么选定Database view without file support。

  选好数据库支持之后,Database Source 按钮会被激活,选中它去调用Data Options对话框。在Database Options对话框中会显示出已向ODBC注册的数据库资源,选定所要操作的数据库,如:Super_ES,单击OK后出现Select Database Tables对话框,其中列举了选中的数据库包含的全部表;选择要操作的表后,单击OK。在选定了数据库和数据表之后,就可以按照惯例继续进行AppWizard操作。

  特别需要指出的是:在生成的应用程序框架View类(如:CSuper_ESView)中,包含一个指向CSuper_ESSet对象的指针m_pSet,该指针由AppWizard建立,目的是在视表单和记录集之间建立联系,使得记录集中的查询结果能够很容易地在视表单上显示出来。

  要使程序与数据源建立联系,需用CDateBase::OpenEx()或CDatabase::Open()函数来进行初始化。数据库对象必须在使用它构造记录集对象之前初始化。

 三、实例

  1.查询记录

  查询记录使用CRecordSet::Open()和CRecordSet::Requery()成员函数。在使用CRecordSet类对象之前,必须使用CRecordSet::Open()函数来获得有效的记录集。一旦已经使用过CRecordSet::Open()函数,再次查询时就可以应用CRecordSet::Requery()函数。

  在调用CRecordSet::Open()函数时,如果将一个已经打开的CDatabase对象指针传给CRecordSet类对象的m_pDatabase成员变量,则使用该数据库对象建立ODBC连接;否则如果m_pDatabase为空指针,就新建一个CDatabase类对象,并使其与缺省的数据源相连,然后进行CRecordSet类对象的初始化。缺省数据源由GetDefaultConnect()函数获得。也可以提供所需要的SQL语句,并以它来调用CRecordSet::Open()函数,例如:Super_ESSet.Open(AFX_DATABASE_USE_DEFAULT,strSQL);

  如果没有指定参数,程序则使用缺省的SQL语句,即对在GetDefaultSQL()函数中指定的SQL语句进行操作:

  CString CSuper_ESSet::GetDefaultSQL()
  {return _T(″[BsicData],[MinSize]″);}


  对于GetDefaultSQL()函数返回的表名,对应的缺省操作是SELECT语句,即:

  SELECT *FROM BasicData,MainSize


  在查询过程中,也可以利用CRecordSet的成员变量m_strFilter和m_strSort来执行条件查询和结果排序。m_strFilter为过滤字符串,存放着SQL语句中WHERE后的条件串;m_strSort为排序字符串,存放着SQL语句中ORDER BY后的字符串。如:

  Super_ESSet.m_strFilter=″TYPE=‘电动机’″;
  Super_ESSet.m_strSort=″VOLTAGE″;
  Super_ESSet.Requery();


  对应的SQL语句为:

  SELECT *FROM BasicData,MainSize
  WHERE TYPE=‘电动机’
  ORDER BY VOLTAGE


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

  S声明参变量:

  CString p1;
  float p2;


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

  p1=_T(″″);
  p2=0.0f;
  m_nParams=2;


  S将参变量与对应列绑定:

  pFX->SetFieldType(CFieldExchange::param)

  RFX_Text(pFX,_T(″P1″),p1);
  RFX_Single(pFX,_T(″P2″),p2);


  完成以上步骤后就可以利用参变量进行条件查询:

  m_pSet->m_strFilter=″TYPE=? AND VOLTAGE=?″;m_pSet->p1=″电动机″;
  m_pSet->p2=60.0; 
  m_pSet->Requery();


  参变量的值按绑定的顺序替换查询字串中的“?”通配符。

  如果查询的结果是多条记录,可以用CRecordSet类的函数Move()、MoveNext()、MovePrev()、MoveFirst()和MoveLast()来移动光标。

  2.增加记录

  增加记录使用AddNew()函数,要求数据库必须是以允许增加的方式打开:

  m_pSet->AddNew(); //在表的末尾增加新记录
  m_pSet->SetFieldNull(&(m_pSet->m_type), FALSE);
  m_pSet->m_type=″电动机″;
  ……
  //输入新的字段值
  m_pSet->Update();
  //将新记录存入数据库
  m_pSet->Requery();
  //重建记录集


  3.删除记录

  可以直接使用Delete()函数来删除记录,并且在调用Delete()函数之后不需调用Update()函数:

  m_pSet->Delete();
  if (!m_pSet->IsEOF())
  m_pSet->MoveNext();
  else
  m_pSet->MoveLast();


  4.修改记录

  修改记录使用Edit()函数:

  m_pSet->Edit();
  //修改当前记录
  m_pSet->m_type=″发电机″;
  //修改当前记录字段值
 ……
  m_pSet->Update(); //将修改结果存入数据库
  m_pSet->Requery();


  5.撤消操作

  如果用户选择了增加或者修改记录后希望放弃当前操作,可以在调用Update()函数之前调用:

  CRecordSet::Move(AFX_MOVE_REFRESH)来撤消增加或修改模式,并恢复在增加或修改模式之前的当前记录。其中,参数AFX_MOVE_REFRESH的值为零。

  6.数据库连接的复用

  在CRecordSet类中定义了一个成员变量m_pDatabase:

  CDatabase* m_pDatabase;


  它是指向对象数据库类的指针。如果在CRecordSet类对象调用Open()函数之前,将一个已经打开的CDatabase类对象指针传给m_pDatabase,就能共享相同的CDatabase类对象。如:

  CDatabase m_db;
  CRecordSet m_set1,m_set2;
  m_db.Open(_T(″Super_ES″)); //建立ODBC连接
  m_set1.m_pDatabase=&m_db;
  //m_set1复用m_db对象
  m_set2.m_pDatabse=&m_db;
  // m_set2复用m_db对象


  7.SQL语句的直接执行

  虽然我们可以通过CRecordSet类完成大多数的查询操作,而且在CRecordSet::Open()函数中也可以提供SQL语句,但是有时候我们还是希望进行一些其他操作,例如建立新表、删除表、建立新的字段等,这时就需要使用CDatabase类直接执行SQL语句的机制。通过调用CDatabase::ExecuteSQL()函数来完成SQL语句的直接执行:

  BOOL CDB::ExecuteSQLAndReportFailure(const CString& strSQL)
  {TRY
  {m_pdb->ExecuteSQL(strSQL);
  //直接执行SQL语句}
  CATCH (CDBException,e)
  {CString strMsg;
  strMsg.LoadString(IDS_EXECUTE_SQL_FAILED);
  strMsg+=strSQL;
  return FALSE;}
  END_CATCH
  return TRUE;}


  应当指出的是,由于不同的DBMS提供的数据操作语句不尽相同,直接执行SQL语句可能会破坏软件的DBMS无关性,因此在应用中应当慎用此类操作。

  8.动态连接表

  表的动态连接可以利用在调用CRecordSet::Open()函数时指定SQL语句来实现。同一个记录集对象只能访问具有相同结构的表,否则查询结果将无法与变量相对应。

void CDB::ChangeTable()
{
 if (m_pSet->IsOpen()) m_pSet->Close();
 switch (m_id)
 {
  case 0:
   m_pSet->Open(AFX_DB_USE_DEFAULT_TYPE,″SELECT * FROM SLOT0″);
   //连接表SLOT0
   m_id=1;
   break;
  case 1:
   m_pSet->Open(AFX_DB_USE_DEFAULT_TYPE,″SELECT FROM SLOT1″); //连接表SLOT1
   m_id=0;
   break;
 }
}


  9.动态连接数据库

  可以通过赋与CRecordSet类对象参数m_pDatabase来连接不同数据库的CDatabase对象指针,从而实现动态连接数据库。

void CDB::ChangeConnect()
{
 CDatabase* pdb=m_pSet->m_pDatabase;
 pdb->Close();
 switch (m_id)
 {
  case 0:
   if (!pdb->Open(_T(″Super_ES″)))
    //连接数据源Super_ES
   {
    AfxMessageBox(″数据源Super_ES打开失败″,″请检查相应的ODBC连接″, MB_OK|MB_ICONWARNING);
    exit(0);
   }
   m_id=1;
   break;
  case 1:
   if (!pdb->Open(_T(″Motor″)))
   //连接数据源Motor
   {
    AfxMessageBox(″数据源Motor打开失败″,″请检查相应的ODBC连接″, MB_OK|MB_ICONWARNING);
    exit(0);
   }
   m_id=0;
   break;
 }
}


  总结:

  Visual C++中的ODBC类库可以帮助程序员完成绝大多数的数据库操作。利用ODBC技术使得程序员从具体的DBMS中解脱出来,从而可以减少软件开发的工作量,缩短开发周期,并提高效率和软件的可靠性。

原文地址:https://www.cnblogs.com/inory/p/5468681.html