一、ADO概述

ADO即Microsoft ActiveXData Object,是Microsoft继ODBC之后,基于OLE DB技术的一种数据库操作技术,使您能够编写通过 OLE DB提供者对在数据库服务器中的数据进行访问和操作的应用程序。ADO同OLE DB、数据库之间的关系可以用下图来表示:

其主要优点是易于使用、高速度、低内存支出和占用磁盘空间较少。ADO支持用于建立基于客户端/服务器和 Web的应用程序的主要功能。

二、ADO初步认识

在此介绍的ADO编程技术都是基于C++的,要熟练掌握如何用ADO操作数据库,首先对ADO技术得有个总体的了解,AD0主要由几个对象组成:Connection、Command、Paramenter、Recorderset、Fields、Error、Property对象,对数据库的操作,都是通过这几个对象来进行的,对象模型的关系图如下:

·      Connection对象

在数据库应用里操作数据源都必须通过该对象,这是数据交换的环境。Connection对象代表了同数据源的一个会话,在客户/服务器模型里,这个会话相当于同服务器的一次网络连接。不同的数据提供者提供的该对象的集合、方法和属性不同。

借助于Connection对象的集合、方法和属性,可以使用Open和Close方法建立和释放一个数据源连接。使用Execute方法可以执行一个数据操作命令,使用BeginTrans、CommitTrans和RollbackTrans方法可以启动、提交和回滚一个处理事务。通过操作the Errors 集合可以获取和处理错误信息,操作CommandTimeout属性可以设置连接的溢出时间,操作ConnectionString属性可以设置连接的字符串,操作Mode属性可以设置连接的模式,操作Provider属性可以指定OLE DB提供者。

·      Command对象

Command对象是一个对数据源执行命令的定义,使用该对象可以查询数据库并返回一个Recordset对象,可以执行一个批量的数据操作,可以操作数据库的结构。不同的数据提供者提供的该对象的集合、方法和属性不同。

借助于Command对象的集合、方法和属性,可以使用Parameters集合制定命令的参数,可以使用Execute方法执行一个查询并将查询结果返回到一个Recordset对象里,操作CommandText属性可以为该对象指定一个命令的文本,操作CommandType属性可以指定命令的类型,操作Prepared可以得知数据提供者是否准备好命令的执行,操作CommandTimeout属性可以设置命令执行的溢出时间。

·      Parameter对象

Parameter对象在Command对象中用于指定参数化查询或者存储过程的参数。大多数数据提供者支持参数化命令,这些命令往往是已经定义好了的,只是在执行过程中调整参数的内容。

借助于Parameter对象的集合、方法和属性,可以通过设置Name属性指定参数的名称,通过设置Value属性可以指定参数的值,通过设置Attributes和Direction、Precision、NumericScale、Size与Type属性可以指定参数的信息,通过执行AppendChunk方法可以将数据传递到参数里。

·      Recordset对象

如果执行的命令是一个查询并返回存放在表中的结果集,这些结果集将被保存在本地的存储区里,Recordset对象是执行这种存储的ADO对象。通过Recordset对象可以操纵来自数据提供者的数据,包括修改和更新行、插入和删除行。

ADO定义了如表

光标类型

描述

adOpenDynamic

允许添加、修改和删除记录,支持所有方式的光标移动,其他用户的修改可以在联机以后仍然可见

adOpenKeyset

类似于adOpenDynamic光标,它支持所有类型的光标移动,但是建立连接以后其他用户对记录的添加不可见,其他用户对记录的删除和对数据的修改是可见的。支持书签操作

adOpenStatic

支持各种方式的光标移动,但是建立连接以后其他用户的行添加、行删除和数据修改都不可见,支持书签操作

adOpenForwardOnly

只允许向前存取,而且在建立连接以后,其他用户的行添加、行删除和数据修改都不可见,支持书签操作

ADO定义了如表

锁定类型

描述

adLockReadOnly

(缺省)数据只读

adLockPessimistic

锁定操作的所有行,也称为消极锁定

adLockOptimistic

只在调用Update方法时锁定操作的行,也称为积极锁定

adLockBatchOptimistic

在批量更新时使用该锁定,也称为积极批量锁定

ADO定义了如表

光标服务位置

描述

adUseNone

不使用光标服务位置

adUseClient

使用客户端光标

adUseServer

(缺省)使用数据服务端或者驱动提供端光标

借助于Recordset对象的集合、方法和属性,可以通过设置CursorType属性设置记录集的光标类型,通过设置CursorLocation属性可以指定光标位置,通过读取BOF和EOF属性的值,获知当前光标在记录集里的位置是在最前或者最后,通过执行MoveFirst、MoveLast、MoveNext和MovePrevious方法移动记录集里的光标,通过执行Update方法可以更新数据修改,通过执行AddNew方法可以执行行插入操作,通过执行Delete方法可以删除行。

·      Field对象

Recordset对象的一个行由一个或者多个Fields对象组成,如果把一个Recordset对象看成一个二维网格表,那么Fields对象就是这些列。这些列里保存了列的名称、数据类型和值,这些值是来自数据源的真正数据。为了修改数据源里的数据,必须首先修改Recordset对象各个行里Field对象里的值,最后Recordset对象将这些修改提交到数据源。

借助于Field对象的集合、方法和属性,可以通过读取Name属性,获知列的名称。通过操作Value属性可以改变列的值,通过读取Type、Precision和NumericScale属性,可获知列的数据类型、精度和小数位的个数,通过执行AppendChunk和GetChunk方法可以操作列的值。

·      Error对象

Error对象包含了ADO数据操作时发生错误的详细描述,ADO的任何对象都可以产生一个或者多个数据提供者错误,当错误发生时,这些错误对象被添加到Connection对象的Errors集合里。当另外一个ADO对象产生一个错误时,Errors集合里的Error对象被清除,新的Error对象将被添加到Errors集合里。

借助于Errosr对象的集合、方法和属性,可以通过读取Number和Description属性,获得ADO错误号码和对错误的描述,通过读取Source属性得知错误发生的源。

·      Property对象

Property对象代表了一个由提供者定义的ADO对象的动态特征。ADO对象有两种类型的Property对象:内置的和动态的。内置的Property对象是指那些在ADO里实现的在对象创建时立即可见的属性,可以通过域作用符直接操作这些属性。动态的Property对象是指由数据提供者定义的底层的属性,这些属性出现在ADO对象的Properties集合里,例如,如果一个Recordset对象支持事务和更新,这些属性将作为Property对象出现在Recordset对象的Properties集合里。动态属性必须通过集合进行引用,比如使用下面的语法:

MyObject.Properties(0)

或者

MyObject.Properties("Name")

不能删除任何类型的属性对象。借助于Property对象的集合、方法和属性,可以通过读取Name属性获得属性的名称,通过读取Type属性获取属性的数据类型,通过读取Value属性获取属性的值。

三、ADO编程

ADO是以DLL封装的,要使用ADO,首先得引入其DLL库,引入ADO类型库的方法有多种,在此我们就介绍一种----通过预处理指令#import引入。一般情况下,在windows操作系统的Program Files\Common Files\System\ado目录下都有一个msado*.dll文件,根据windows版本不同,该文件可以是msado1.dll、msado15.dll、msado2.dll,对于此文件,版本不一样,可能导致一些冲突,如:系统自带的msado*.dll可能与程序编译所用的版本不一样,可能会导致一些异常,那么对于这种情况,我个人一般的做法是,在软件目录下面带上msado*.dll文件,这样就排除了对软件运行环境的依赖。

Ado类型库引入后,程序在编译过程中,VC++会读出msado*.dll中的类型库信息,自动产生两个该类型库的头文件和实现文件msado15.tlh和msado15.tli(在您的Debug或Release目录下)。在这两个文件里定义了ADO的所有对象和方法,以及一些枚举型的常量等。以下是引入方法:

#import"..\CommFile\Lib\msado15.dll" named_guids rename("EOF","adoEOF"),rename("BOF","adoBOF")

//重命名EOF与BOF以免与其他命名空间冲突

但要注意不能放在stdAfx.h文件的开头,而应该放在所有include指令的后面。否则在编译时会出错。

初始化COM

ADO是COM组件,使用任何COM组件的时候,都要先初始化COM,其实就是在使用COM组件前调用一个API----CoInitialize(),在使用ADO对象之前调用即可,以下为代码:

HRESULT hr =CoInitialize(NULL);

ASSERT(SUCCEEDED(hr));//返回值可判断初始化COM是否成功,请用SUCCEEDED来判断

一般情况下,使用完COM之后,调用反初始化COM函数----CoUninitialize()。一般会在数据库访问封装类的析构函数中调用。

三个核心对象

ADO的3个核心对象是连接对象(_Connection)、命令对象(_Command)和记录集对象(_RecordSet)。其中连接对象是任何操作必须的。很多操作3个核心对象都可以完成。要实例化它们并使用它们提供的方法,不得不说到它们是一种智能指针(Smart Pointer)。在初始化或释放等操作时,它们是一个对象,用点操作符,其他大部分操作则使用“->”操作符。

实例化

_ConnectionPtr pConn(__uuidof(Connection));

_RecordsetPtr pRec(__uuidof(Recordset));

_CommandPtr pCmd(__uuidof(Command));

如果上面不加参数,则需加上:

pConn.CreateInstance("ADODB.Connection");

pRec.CreateInstance("ADODB.Recordset");

pCmd.CreateInstance("ADODB.Command");

有些数据库操作_Connection一个就能完全搞定。如update语句。因为它不需要返回的结果。也有些操作需要返回记录集,如select语句。这里就至少需要_Connection和_Recordset两种核心对象,也可以用_Command执行之。

错误捕获

数据库操作难免出现错误,连接串错误,SQL语句错误,或返回NULL你却硬要向表里塞(此错误可以在取出后用var.vt!=VT_NULL判断),所以我们需要把它们放到try…catch段中。ADO在捕获到错误后会抛出_com_error类型异常,我们可以这样做:

try

{

pConn->ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password=123;Initial Catalog=cfdata";

pConn->Open("","","",adConnectUnspecified);

…………   //代码省略

}

catch(_com_error& e)

{

AfxMessageBox(e.ErrorMessage());

AfxMessageBox(e.Description());

}

这里有个疑惑,在捕获错误后,e.ErrorMessage()和e.Description()中放着不同信息,有时前者说得清晰,有的后者说的清晰,搞不清楚,索性就都加上吧。最后,可以再加个catch(…),毕竟ADO之外的地方也可能发生错误。

这里,我还犯过一个错误,被它整了N久。我把_com_error& e写成了_com_error* e后面也对应改成->操作符,而且编译通过,结果一运行程序就崩溃,而且它不告诉我在哪出错,因为这时的错误是_com_error这时却对着_com_error*来捕获当然捕不到。这里&只是一个引用,写不写无所谓,*是万万不可地。(网上书上很多大师级代码都是用了*,误导啊)。

VARIANT和BSTR,_variant_t和_bstr_t
COM必须设计成跨平台,需要提供语言无关的数据类型,多数编程语言有自己的字符串表示,需要定义一种通用的字符串类型,可以很容易的匹配到不同编程语言。
C++中,由于COM编程不使用CString,因为它需要一种更普遍的方法来处理字符串和其他数据类型,这就是VARIANT变量数据和BSTR类型的来历。

Variant 是一种特殊的数据类型,除了定长 String 数据及用户定义类型外,可以包含任何种类的数据。_variant_t是一个类,它封装了VARIANT类型。用ADO读取数据库中数据的

时候一般用_variant_t。
VARIANT的定义和用法可以参见tagVARIANT结构体
----------------------------------------------------------------------------
typedef struct tagVARIANT {
VARTYPE vt;
unsigned short wReserved1;
unsigned short wReserved2;
unsigned short wReserved3;
union {
unsigned char bVal; // VT_UI1.
short iVal; // VT_I2 .
long lVal; // VT_I4 .
……………………………………………………………………………………
void FAR* byref; // Generic ByRef.
};
};
-----------------------------------------------------------------------------
下面是使用VARIANT的例子:
VARIANT theValue;
//Initializes a variant——::VariantInit(&theValue);
int value =2007;
theValue.vt = VT_I4; // 声明是long类型(long lVal; // VT_I4)
theValue.lVal = value; // 赋值

如果theValue.vt=VT_ERROR;//没有初始化
如果theValue.vt=VT_NULL; //初始化了,但没有值
如果theValue.vt=VT_EMPTY; //赋值为空

BSTR(Basic STRing,Basic字符串),用来处理COM中的字符串。BSTR是一个字符串指针,可以存放字符串。它的定义如下:
typedef OLECHAR *BSTR;
typedef WCHAR OLECHAR;
_bstr_t也是一个类,它封装了BSTR类型。在操作数据库时,SQL语言可以用_bstr_t来存放。例如:

CString sql="select * from table";
m_pCnn->Open(_bstr_t(sql),"","",-1);

或者:
_bstr_t sql="select * from table";
m_pCnn->Open(sql,"","",-1);

或者:
CString sql="select * from table";
BSTR bstrSQL=sql.AllocSysString();
m_pCnn->Open(bstrSQL,"","",-1);

除了_variant_t,_bstr_t之外,_com_error也是ado操作中常用到的COM支持类,在comdef.h中定义,使用这些类,可以避免在组件中引起的冲突。

ADO学习(一)基础理论相关推荐

  1. ADO学习(八)源码示例

    #include <icrsint.h> //其中icrsint.h文件包含了VC++扩展的一些预处理指令.宏等的定义,用于COM编程时使用. //放在所有include的后面 //#im ...

  2. ADO学习(四)ADO扩展IADORecordBinding

    当我们使用Visual C++进行ADO编程时,一项颇为头疼的工作就是对VARIANT字段类型的处理.通常做法是,先把VARIANT类型转换为形式上较为类似的C++类型,然后再把转换后的数据存放在一个 ...

  3. ADO学习(九)如何阅读ADO文档

      <ADO API参考>用VB的语法描述了ADO API的内容.但ADO程序员却使用着不同的编程语言,比如VB,VC++,VJ++.对此<ADO for VC++的语法索引> ...

  4. ADO学习(三)Command 对象

    Command 对象 ADO Command 对象用于执行面向数据库的一次简单查询.此查询可执行诸如创建.添加.取回.删除或更新记录等动作.如果该查询用于取回数据,此数据将以一个 RecordSet  ...

  5. ADO学习(六)服务器和客户端游标

    ADO连接数据库的操作中遇到CursorLocation的adUseClient和adUseServer这两个属性值,收集了些资料,帮助理解. 服务器端游标将结果集放在服务器的缓冲区内,客户端游标将结 ...

  6. ADO学习(七)ADOX相关知识

    一.ADOX 概述 Microsoft? ActiveX? Data Objects Extensions for Data Definition Language and Security (ADO ...

  7. 深度学习相关基础理论

    反向传播与梯度下降算法 在深度学习中最重要的算法莫过于反向传播算法(Back Propagation,BP)和梯度下降算法(Gradient Descent,GD),从宏观上理解深层神经网络中的这两个 ...

  8. ACCESS+ADO学习记录一点点

    最近做了个关于access数据库的小东西,网上说用ADO操作比较好,通用性更好,所以就往这个方向查资料了,网上资料挺多大体操作如下: 1.在MFC工程里的StdAfx.h头文件中加入 //引入支持AD ...

  9. 重新学习计算机基础理论知识(后知后觉)

    在写了几年代码之后,才知道有软考这样的考试存在:在写了很多逻辑之后,总是对计算机如何处理程序几无所知:在大学时学的东西完全没有了印象和记忆,程序一旦开始运行就好像丢进自动贩卖机的硬币一样再也看不到它的 ...

最新文章

  1. Java 缺省适配器模式
  2. 弹出确定_Redmi K30 Pro再剧透:弹出式全面屏,没有高刷
  3. boost::mp11::mp_copy_if相关用法的测试程序
  4. jzoj3379-查询【主席树】
  5. jdbc驱动程序_JDBC操作数据库的步骤
  6. Convert.ToDateTime(值),方法可以把一个值转化成DateTime类型。
  7. 使用MSTest v2进行单元测试的并行化
  8. python3正则表达式判断ipv4_Python 正则表达式验证IPv4地址
  9. poi导出Excel,表格画斜线,并设置数据
  10. 怎么把录音导入库乐队_怎么往库乐队里导入音乐
  11. css 响应式布局(媒体查询),兼容pc,ipad,移动端的布局单位
  12. 【Arduino】wokwi在线编程仿真学习
  13. JS 之 图片编辑器插件
  14. 历届蓝桥杯Scratch编程省赛 初级 中级 青少年编程比赛省赛真题解析【持续更新 已更新至35题】
  15. 用循环输出二十六个小写英文字母的内容(需要类型转换)--java作业
  16. 微软官方安装U盘启动盘制作方法教程
  17. 网页的灵魂,游荡在人机之间的互动——Vuejs动画体验
  18. ROS环境问题:多个工作空间的环境变量问题(source .bashrc配置文件)
  19. vue根据银行账号识别银行卡信息
  20. 【数方大数据】什么是三网运营商大数据精准获客?

热门文章

  1. 数据库-优化-数据库结构的优化-数据类型
  2. 哨兵机制服务器环境准备
  3. Spring Boot配置文件有提示
  4. 解决IntelliJ IDEA下Maven报错Unknown lifecycle phase “.test.skip=true“. You must specify a valid lifecycle
  5. 俄罗斯“指尖旋风”席卷南京
  6. 网络原理(四)-----动态路由协议篇
  7. 软件开发中的几种数据交换协议
  8. ios app上架被拒的问题
  9. 几种Win7/Windows共享虚拟无线网络的方法及区别
  10. 打包,并自动安装SQL数据库