ADO 开发指南(程式猎人收集整理)----发现最好的ADO MFC的数据库操作

ADO第一次亲密接触 -- ADO开发实践之一

 

一、ADO简介

ADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE
DB之上的高层数据库访问技术,请不必为此担心,即使你对OLE DB,COM不了解也能
轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都
要容易使用,并不失灵活性。本文将详细地介绍在VC下如何使用ADO来进行数据库应
用程序开发,并给出示例代码。

本文示例代码

 

二、基本流程

万事开头难,任何一种新技术对于初学者来说最重要的还是“入门”,掌握其要点。
让我们来看看ADO数据库开发的基本流程吧!

(1)初始化COM库,引入ADO库定义文件

(2)用Connection对象连接数据库

(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用
Recordset对象取得结果记录集进行查询、处理。

(4)使用完毕后关闭连接释放对象。

 

准备工作:

为了大家都能测试本文提供的例子,我们采用Access数据库,您也可以直接在我们
提供的示例代码中找到这个test.mdb。

下面我们将详细介绍上述步骤并给出相关代码。

【1】COM库的初始化

我们可以使用AfxOleInit()来初始化COM库,这项工作通常在
CWinApp::InitInstance()的重载函数中完成,请看如下代码:

 

 

BOOL CADOTest1App::InitInstance()

 {

 AfxOleInit();

 ......

 

【2】用#import指令引入ADO类型库

我们在stdafx.h中加入如下语句:(stdafx.h这个文件哪里可以找到?你可以在
FileView中的Header Files里找到)

 

#import "c:\program files\common files\system\ado\msado15.dll"
no_namespace rename("EOF","adoEOF")

这一语句有何作用呢?其最终作用同我们熟悉的#include类似,编译的时候系统会
为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。


 

几点说明:

(1) 您的环境中msado15.dll不一定在这个目录下,请按实际情况修改

(2) 在编译的时候肯能会出现如下警告,对此微软在MSDN中作了说明,并建议我们
不要理会这个警告。

msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned
type, result still unsigned

 

【3】创建Connection对象并连接数据库

首先我们需要添加一个指向Connection对象的指针:

_ConnectionPtr m_pConnection;

下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕
捉。

 

 

BOOL CADOTest1Dlg::OnInitDialog()

 {

 CDialog::OnInitDialog();

 HRESULT hr;

 try

 {

 hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection
对象

 if(SUCCEEDED(hr))

 {

 hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=test.mdb","","",adModeUnknown);///连接数据库

 ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,
需要改为:Provider=Microsoft.Jet.OLEDB.3.51;

 }

 }

 catch(_com_error e)///捕捉异常

 {

 CString errormessage;

 errormessage.Format("连接数据库失败!\r\n错误信
息:%s",e.ErrorMessage());

 AfxMessageBox(errormessage);///显示错误信息

 }

 

在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是
该方法的原型


HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID,
_bstr_t Password, long Options )

ConnectionString为连接字串,UserID是用户名, Password是登陆密码,Options是
连接选项,用于指定Connection对象对数据的更新许可权,

Options可以是如下几个常量:

adModeUnknown:缺省。当前的许可权未设置

adModeRead:只读

adModeWrite:只写

adModeReadWrite:可以读写

adModeShareDenyRead:阻止其它Connection对象以读权限打开连接

adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接

adModeShareExclusive:阻止其它Connection对象以读写权限打开连接

adModeShareDenyNone:阻止其它Connection对象以任何权限打开连接

 

我们给出一些常用的连接方式供大家参考:

(1)通过JET数据库引擎对ACCESS2000数据库的连接

 

m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=C:\\test.mdb","","",adModeUnknown);

 

(2)通过DSN数据源对任何支持ODBC的数据库进行连接:

 

m_pConnection->Open("Data
Source=adotest;UID=sa;PWD=;","","",adModeUnknown);

 

(3)不通过DSN对SQL SERVER数据库进行连接:

m_pConnection->Open("driver={SQL
Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown);

 

其中Server是SQL服务器的名称,DATABASE是库的名称

 

Connection对象除Open方法外还有许多方法,我们先介绍Connection对象中两个有
用的属性ConnectionTimeOut与State

ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如:

m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒

m_pConnection->Open("Data Source=adotest;","","",adModeUnknown);

 

 

State属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以
通过读取这个属性来作相应的处理,例如:


 

if(m_pConnection->State)

 m_pConnection->Close(); ///如果已经打开了连接则关闭它

 

 

【4】执行SQL命令并取得结果记录集

为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr
m_pRecordset;

并为其创建Recordset对象的实例:
m_pRecordset.CreateInstance("ADODB.Recordset");

SQL命令的执行可以采用多种形式,下面我们一进行阐述。

 

(1)利用Connection对象的Execute方法执行SQL命令

Execute方法的原型如下所示:

_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT *
RecordsAffected, long Options ) 其中CommandText是命令字串,通常是SQL命令。
参数RecordsAffected是操作完成后所影响的行数, 参数Options表示CommandText
中内容的类型,Options可以取如下值之一:

adCmdText:表明CommandText是文本命令

adCmdTable:表明CommandText是一个表名

adCmdProc:表明CommandText是一个存储过程

adCmdUnknown:未知

 

Execute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。

 _variant_t RecordsAffected;

 ///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符
串username,整形old,日期型birthday

 m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old
INTEGER,birthday DATETIME)",&RecordsAffected,adCmdText);

 ///往表格里面添加记录

 m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday)
VALUES (1, 'Washington',25,'1970/1/1')",&RecordsAffected,adCmdText);

 ///将所有记录old字段的值加一

 m_pConnection->Execute("UPDATE users SET old =
old+1",&RecordsAffected,adCmdText);

 ///执行SQL统计命令得到包含记录条数的记录集

 m_pRecordset = m_pConnection->Execute("SELECT COUNT(*) FROM
users",&RecordsAffected,adCmdText);

 _variant_t vIndex = (long)0;

 _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段
的值放入vCount变量


 m_pRecordset->Close();///关闭记录集

 CString message;

 message.Format("共有%d条记录",vCount.lVal);

 AfxMessageBox(message);///显示当前记录条数

 

 

(2)利用Command对象来执行SQL命令

 

 _CommandPtr m_pCommand;

 m_pCommand.CreateInstance("ADODB.Command");

 _variant_t vNULL;

 vNULL.vt = VT_ERROR;

 vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数

 m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立
的连接赋值给它

 m_pCommand->CommandText = "SELECT * FROM users";///命令字串

 m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命
令,取得记录集

 

在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进
行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。

 

 

(3)直接用Recordset对象进行查询取得记录集

例如

 

 m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch
*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);

 

Open方法的原型是这样的:

HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t &
ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum
LockType, long Options )

其中:

①Source是数据查询字符串

②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造
一个_variant_t对象)

③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:

enum CursorTypeEnum

{

adOpenUnspecified = -1,///不作特别指定


adOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用
MoveNext向前滚动,这种方式可以提高浏览速度。但诸如
BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用

adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,
但对于更新原有记录的操作对你是可见的。

adOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反
应出来。

adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的
新增、删除、更新操作对你的记录集来说是不可见的。

};

④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:

enum LockTypeEnum

{

adLockUnspecified = -1,///未指定

adLockReadOnly = 1,///只读记录集

adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最
安全的锁定机制

adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在
此之前仍然可以做数据的更新、插入、删除等动作

adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及
删除是在批处理模式下完成。

};

⑤Options请参考本文中对Connection对象的Execute方法的介绍

 

 

 

【5】记录集的遍历、更新

根据我们刚才通过执行SQL命令建立好的users表,它包含四个字
段:ID,username,old,birthday

以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,
移动光标到第二条记录,更改其年龄,保存到数据库。

 

 

_variant_t vUsername,vBirthday,vID,vOld;

_RecordsetPtr m_pRecordset;

m_pRecordset.CreateInstance("ADODB.Recordset");

m_pRecordset->Open("SELECT * FROM
users",_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);

while(!m_pRecordset->adoEOF)///这里为什么是adoEOF而不是EOF呢?还记得
rename("EOF","adoEOF")这一句吗?


{

 vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0
开始计数,你也可以直接给出列的名称,如下一行

 vUsername = m_pRecordset->GetCollect("username");///取得username字段的值

 vOld = m_pRecordset->GetCollect("old");

 vBirthday = m_pRecordset->GetCollect("birthday");

 ///在DEBUG方式下的OUTPUT窗口输出记录集中的记录

 if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL &&
vBirthday.vt != VT_NULL)

 TRACE("id:%d,姓名:%s,年龄:%d,生
日:%s\r\n",vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday);

 m_pRecordset->MoveNext();///移到下一条记录

}

m_pRecordset->MoveFirst();///移到首条记录

m_pRecordset->Delete(adAffectCurrent);///删除当前记录

///添加三条新记录并赋值

for(int i=0;i<3;i++)

{

 m_pRecordset->AddNew();///添加新记录

 m_pRecordset->PutCollect("ID",_variant_t((long)(i+10)));

 m_pRecordset->PutCollect("username",_variant_t("叶利钦"));

 m_pRecordset->PutCollect("old",_variant_t((long)71));

 m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15"));

}

m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录
往下移动一条记录,即移动到第二条记录处

m_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改
其年龄

m_pRecordset->Update();///保存到库中

 

【6】关闭记录集与连接

记录集或连接都可以用Close方法来关闭

 m_pRecordset->Close();///关闭记录集

 m_pConnection->Close();///关闭连接

 

后记:限于篇幅ADO中的许多内容还没有介绍,下次我们将详细介绍Recordset对象
的属性、方法并解决几个关键的技术:绑定方式处理记录集数据、存储过程的调用、
事务处理、图象在数据库中的保存与读取、与表格控件的配合使用等。

下次再见吧!

 


使用ADO实现BLOB数据的存取 -- ADO开发实践之二

 

 

一、前言

在上一篇文章《ADO第一次亲密接触》中我们详细介绍了ADO基本的操作方法,在实
际的开发过程中我们常常需要存储较大的二进制数据对象,比如:图像、音频文件、
或其它二进制数据,这些数据我们称之为二进制大对象BLOB(Binary Large Object),
其存取的方式与普通数据有所区别。本文将介绍利用ADO在数据库中存取BLOB数据的
具体实现过程,并给出实现图像存取显示的完整示例工程。

 

 

二、前期准备

首先我们建立一张名为userinfo的表,包含三个字段:id,username,old,photo,其中
photo是一个可以存储二进制数据的字段。

2.1 在SQL SERVER中我们可以在Query Analyzer中直接输入如下语句创建:

 

 

CREATE TABLE [dbo].[userphoto] (

 [id] [int] IDENTITY (1, 1) NOT NULL ,

 [username] [varchar] (50) NULL ,

 [old] [int] NULL ,

 [photo] [image] NULL

) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

其中photo我们定义为image类型的字段。

 

 

2.2 在ACCESS中创建的方法如下:

建立一张新表包括id,username,old,photo四个字段,然后打开表,选视图菜单中设
计视图,将id设置为自动编号的递增长整型,username为文本,old为数字,photo
为OLE对象。

在我们的示例工程中已经包含了一个建立好的ACCESS2000的库,你可以直接拿来使
用。

 

 

三、具体步骤

3.1 BLOB数据的保存

BLOB类型的数据无法用普通的方式进行存储,我们需要使用AppendChunk函数,
AppendChunk包含在Field对象中,原型如下:

HRESULT AppendChunk (const _variant_t & Data );

从函数原型中可以看到关键的问题是我们需把二进制数据赋值给VARIANT类型的变
量,下面我们给出具体的代码并作简单的分析:


 

 

 

 

///假设m_pBMPBuffer指针指向一块长度为m_nFileLen的二进制数据,并且已经成功
打开了记录集对象m_pRecordset///

 

char *pBuf = m_pBMPBuffer;

VARIANT varBLOB;

SAFEARRAY *psa;

SAFEARRAYBOUND rgsabound[1];

 

m_pRecordset->AddNew(); ///
添加新记录

m_pRecordset->PutCollect("username",_variant_t("小李")); ///
为新记录填充username字段

m_pRecordset->PutCollect("old",_variant_t((long)28); ///
填充old字段

if(pBuf)

{

 rgsabound[0].lLbound = 0;

 rgsabound[0].cElements = m_nFileLen;

 psa = SafeArrayCreate(VT_UI1, 1, rgsabound); ///
创建SAFEARRAY对象

 for (long i = 0; i < (long)m_nFileLen; i++)

 SafeArrayPutElement (psa, &i, pBuf++); ///
将pBuf指向的二进制数据保存到SAFEARRAY对象psa中

 varBLOB.vt = VT_ARRAY | VT_UI1; ///
将varBLOB的类型设置为BYTE类型的数组

 varBLOB.parray = psa; ///
为varBLOB变量赋值

 m_pRecordset->GetFields()->GetItem("photo")->AppendChunk(varBLOB);///
加入BLOB类型的数据

}

m_pRecordset->Update(); ///
保存我们的数据到库中

至此我们的数据已经成功地保存到了数据库中,接下来我们所要做的工作便是将该
数据提取出来,让我们继续吧!

 

3.2 BLOB数据的读取

对应于保存数据时我们所使用的AppendChunk函数,读取数据应该使用GetChunk函


数,GetChunk的原型如下:

_variant_t GetChunk (long Length );

给出数据的长度后GetChunk将返回包含数据的VARIANT类型变量,然后我们可以利用
SafeArrayAccessData函数得到VARIANT变量中指向数据的char *类型的指针,以方
便我们的处理,具体代码如下:

 

 

 

long lDataSize =
m_pRecordset->GetFields()->GetItem("photo")->ActualSize;///得到数据的长

if(lDataSize > 0)

{

 _variant_t varBLOB;

 varBLOB =
m_pRecordset->GetFields()->GetItem("photo")->GetChunk(lDataSize);

 if(varBLOB.vt == (VT_ARRAY | VT_UI1))
///判断数据类型是否正确

 {

 char *pBuf = NULL;

 SafeArrayAccessData(varBLOB.parray,(void **)&pBuf);
///得到指向数据的指针

 /*****在这里我们可以对pBuf中的数据进行处理*****/

 SafeArrayUnaccessData (varBLOB.parray);

 }

}

以上我们成功实现了BLOB数据在数据库中的存取,为了让大家有现成的例子可以参
考,本文提供了示例工程,在示例工程中我们在数据库中保存图像数据,并可以对
这些图像进行浏览、修改,该例子还涉及到如何用char *指向的BMP文件数据创建
BITMAP对象然后显示出来。

 

 

 

 

 

 

 

 

 

 

 


在Visual C++中如何利用UDL文件来建立ADO连接

 

 使用通用数据连接文件(*.UDL,以下简称文件)来创建ADO连接,可以和ODBC
一样可视化地定义要连接的数据源,从而实现数据访问的透明性。

 

 

1.使用UDL文件来创建ADO连接

创建ADO的连接,首先要设置ADO连接对象的ConnectionString属性,该属性提供所
要连接的数据库类型、数据所处服务器、要访问的数据库和数据库访问的安全认证
信息。比较专业的方法是在ConnectionString中直接提供以上信息,下面是访问不
同类型数据源设置ConnectionString的标准:

访问ODBC数据

"Provider=MSDASQL;DSN=dsnName;UID=userName;PWD=userPassword;"

访问ORACLE数据库

"Provider=MSDAORA;Data Source=serverName;User ID=userName;
Password=userPassword;"

访问MS SQL数据库

"Provider=SQLOLEDB;Data Source=serverName;Initial Catalog=databaseName;
User ID=userName;Password=userPassword;"

访问ACCESS 数据库

"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=databaseName;User
ID=userName;Password=userPassword;"

上述的连接属性设置标准随着数据源的类型不同而变化,软件用户常常不习惯这种
设置方式,都希望有可视化的数据源设置方法。为此Microsoft提供了通用数据连接
文件(.UDL)来建立和测试ADO连接属性。ADO连接对象可以很方便地使用UDL文件来
连接数据源,下面例子使用my_data1.udl来创建ADO连接。

_ConnectionPtr m_pDBConn;

m_pDBConn.CreateInstance(__uuidof(Connection));

m_pDBConn->ConnectionString ="File Name=c:\mydir\my_data1.udl";

m_pDBConn->Open("","","",NULL);

这样一来无论数据源如何变化,在软件中都可以用统一的方法编程。当数据源改变
时,只要双击相应的udl文件即可可视化地设置数据源,无需更改软件。

因为ADO是COM接口,为了软件的可靠性,打开ADO连接时,可以加入异常处理代码。

try{

m_pDBConn->Open("","","",NULL);

}catch(_com_error &e){

//处理异常的代码

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

m_pDBConn=NULL;

}

因为_ConnectionPtr m_pDBConn是智能指针,应在处理异常代码时将智能指针设为


NULL后将自动将引用计数降为0。

如果不出现异常,只要在使用完m_pDBConn,只要引用Close方法即可。

2.创建你所需的UDL文件

在你所想创建UDL文件的目录中单击右键,选择从菜单 新建|Microsoft 数据连接,
然后将新创建的UDL文件更改为你所希望的文件名(.UDL扩展名不能改变)。

注:如果操作系统是Window 2000,先创建一个文本文件,再将该文本文件的扩展名
改为 "udl"。

然后双击所创建的UDL文件,即可视化地完成数据源的设定。

使用UDL文件必须在系统中先安装Microsoft MDAC,Win 98第二版,Win 2000中都自
动包含了该组件,需要该组件最新版本时可以到Microsoft网站去下载。

 

 

在VC中使用ADO开发数据库应用程序

 

 

1.引入ADO库文件

使用ADO前必须在工程的stdafx.h文件里用直接引入符号#import引入ADO库文件,

以使编译器能正确编译。代码如下所示:

#import "c:\program files\common files\system\ado\msado15.dll"

no—namespaces rename("EOF" adoEOF")

这行语句声明在工程中使用ADO,但不使用ADO的名字空间,并且为了避免冲突,将
EOF改

名为adoEOF。

 

2.初始化OLE/COM库环境

必须注意的是,ADO库是一组COM动态库,这意味应用程序在调用ADO前,必须初始

化OLE/COM库环境。在MFC应用程序里,一个比较好的方法是在应用程序主类的

InitInstance成员函数里初始化OLE/COM库环境。

 

//初始化OLE/COM库环境

 

BOOL CADOApp::InitInstance()

{

if(!AfxOleInit())

{

AfxMessageBox("OLE初始化出错!");

return FALSE;

}

......

}

 


函数AfxOleInit在每次应用程序启动时初始化OLE/COM库环境。

 

3.?ADO接口简介

ADO库包含三个基本接口:

__ConnectionPtr接口、

__CommandPtr接口和、

__RecordsetPtr接口,

 

__ConnectionPtr接口返回一个记录集或一个空指针。通常使用它来创建一个数据连
接或

执行一条不返回任何结果的SQL语句,如一个存储过程。用__ConnectionPtr接口返
回一

个记录集不是一个好的使用方法。通常同CDatabase一样,使用它创建一个数据连接,

后使用其它对象执行数据输入输出操作。

 

__CommandPtr接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的
存储

过程和SQL语句。在使用__CommandPtr接口时,可以利用全局__ConnectionPtr接口,

可以在__CommandPtr接口里直接使用连接串。如果只执行一次或几次数据访问操作,

者是比较好的选择。但如果要频繁访问数据库,并要返回很多记录集,那么,应该
使用

全局__ConnectionPtr接口创建一个数据连接,然后使用__CommandPtr接口执行存储
过程

和SQL语句。

 

__RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多

的控制功能,如记录锁定,游标控制等。同__CommandPtr接口一样,它不一定要使
用一

个已经创建的数据连接,可以用一个连接串代替连接指针赋给__RecordsetPtr的

connection成员变量,让它自己创建数据连接。如果要使用多个记录集,最好的方
法是

同Command对象一样使用已经创建了数据连接的全局—ConnectionPtr接口,然后使

__RecordsetPtr执行存储过程和SQL语句。

 

4.使用__ConnectionPtr接口

 

__ConnectionPtr是一个连接接口,它类似于CDatabase和CDaoDatabase。首先创建


一个

__ConnectionPtr接口实例,接着指向并打开一个ODBC数据源或OLE DB数据提供者

(Provider)。以下代码分别创建一个基于DSN和非DSN的数据连接。

 

//使用__ConnectionPtr(基于DSN)

 

__ConnectionPtr MyDb;

 

MyDb.CreateInstance(__uuidof(Connection));

 

MyDb-〉Open("DSN=samp;UID=admin;PWD=admin","","",-1);

 

//使用—ConnectionPtr (基于非DSN)

 

__ConnectionPtr MyDb;

 

MyDb.CreateInstance(__uuidof(Connection));

 

MyDb-〉Open("Provider=SQLOLEDB;SERVER=server;DATABASE=samp;UID=admin;

 

PWD=admin","","",-1);

 

 

5.使用__RecordsetPtr接口

__RecordsetPtr接口的使用方法和CDaoDatabase类似,通过以下代码的比较,你会
发现

使用—RecordsetPtr接口非常简单(以下代码使用上面已经创建的数据连接):

 

//使用CDaoDatabase执行SQL语句

 

CDaoRecordset MySet = new CDaoRecordset(MyDb);

 

MySet-〉Open(AFX__DAO__USE__DEFAULT__TYPE,"SELECT ? FROM t__samp");

 

Now using ADO:

 

//使用__RecordsetPtr执行SQL语句

 

__RecordsetPtr MySet;

 

MySet.CreateInstance(__uuidof(Recordset));


 

MySet-〉Open("SELECT ? FROM some__table",

 

MyDb.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);

 

现在我们已经有了一个数据连接和一个记录集,接下来就可以使用数据了。从以下

代码可以看到,使用ADO的__RecordsetPtr接口,就不需要像DAO那样频繁地使用大
而复

杂的数据结构VARIANT,并强制转换各种数据类型了,这也是ADO的优点之一。假定
程序

有一个名称为m__List的ListBox控件,下面代码我们用__RecordsetPtr接口获取记
录集

数据并填充这个ListBox控件:

 

//使用ADO访问数据

 

__variant__t Holder

 

try{while(!MySet-〉adoEOF)

 

{ Holder = MySet-〉GetCollect("FIELD__1");

 

if(Holder.vt!=VT__NULL)

 

m__List.AddString((char?)__bstr__t(Holder));

 

MySet-〉MoveNext();} }

 

catch(__com__error ? e)

 

{ CString Error = e-〉ErrorMessage();

 

AfxMessageBox(e-〉ErrorMessage());

 

} catch(...)

 

{ MessageBox("ADO发生错误!");}

 

必须始终在代码中用try和catch来捕获ADO错误,否则ADO错误会使你的应用程序崩

溃。当ADO发生运行错误时(如数据库不存在),OLE DB数据提供者将自动创建一个

__com__error对象,并将有关错误信息填充到这个对象的成员变量。


 

6.使用__CommandPtr接口

__CommandPtr接口返回一个Recordset对象,并且提供了更多的记录集控制功能,以
下代

码示例使用__CommandPtr接口的方法:

 

//使用__CommandPtr接口获取数据

 

__CommandPtr pCommand;

 

__RecordsetPtr MySet;

 

pCommand.CreateInstance(__uuidof(Command));

 

pCommand-〉ActiveConnection=MyDb;

 

pCommand-〉CommandText="select ? from some—table";

 

pCommand-〉CommandType=adCmdText;

 

pCommand-〉Parameters-〉Refresh();

 

MySet=pCommand-〉Execute(NULL,NULL,adCmdUnknown);

 

__variant__t TheValue = MySet-〉GetCollect("FIELD__1");

 

CString sValue=(char?)__bstr__t(TheValue);

 

7.?关于数据类型转换

由于COM对象是跨平台的,它使用了一种通用的方法来处理各种类型的数据,因此

CString 类和COM对象是不兼容的,我们需要一组API来转换COM对象和C++类型的

据。__vatiant__t和__bstr__t就是这样两种对象。它们提供了通用的方法转换COM
对象和C++类型的数据。

 

 

 

 

 

 

 


VC++ ADO开发实践之五 zswzwy(收藏)

 

一、ADO概述

 

ADO是Microsoft为最新和最强大的数据访问范例 OLE DB 而设计的,是一个便于使
用的应用程序层接口。ADO 使您能够编写应用程序以通过 OLE.DB 提供者访问和操
作数据库服务器中的数据。ADO 最主要的优点是易于使用、速度快、内存支出少和
磁盘遗迹小。ADO 在关键的应用方案中使用最少的网络流量,并且在前端和数据源
之间使用最少的层数,所有这些都是为了提供轻量、高性能的接口。之所以称为 ADO,
是用了一个比较熟悉的暗喻,OLE 自动化接口。

 

OLE DB是一组”组件对象模型”(COM) 接口,是新的数据库低层接口,它封装了ODBC
的功能,并以统一的方式访问存储在不同信息源中的数据。OLE DB是Microsoft
UDA(Universal Data Access)策略的技术基础。OLE DB 为任何数据源提供了高性能
的访问,这些数据源包括关系和非关系数据库、电子邮件和文件系统、文本和图形、
自定义业务对象等等。也就是说,OLE DB 并不局限于 ISAM、Jet 甚至关系数据源,
它能够处理任何类型的数据,而不考虑它们的格式和存储方法。在实际应用中,这
种多样性意味着可以访问驻留在 Excel 电子数据表、文本文件、电子邮件/目录服
务甚至邮件服务器,诸如 Microsoft Exchange 中的数据。但是,OLE DB 应用程序
编程接口的目的是为各种应用程序提供最佳的功能,它并不符合简单化的要求。您
需要的API 应该是一座连接应用程序和 OLE DB 的桥梁,这就是 ActiveX Data
Objects (ADO)。

 

二、在VC中使用ADO

 

1、引入ADO库文件

 

使用ADO前必须在工程的stdafx.h文件里用直接引入符号#import引入ADO库文件,以
使编译器能正确编译。代码如下所示:

 

代码1:用#import引入ADO库文件

 

#import "c:\program files\common files\system\ado\msado15.dll"

 

no_namespaces rename("EOF" adoEOF")

 

这行语句声明在工程中使用ADO,但不使用ADO的名字空间,并且为了避免常数冲突,
将常数EOF改名为adoEOF。现在不需添加另外的头文件,就可以使用ADO接口了。

 

2、初始化OLE/COM库环境

 


必须注意的是,ADO库是一组COM动态库,这意味应用程序在调用ADO前,必须初始化
OLE/COM库环境。在MFC应用程序里,一个比较好的方法是在应用程序主类的
InitInstance成员函数里初始化OLE/COM库环境。

 

代码2:初始化OLE/COM库环境

 

BOOL CADOApp::InitInstance()

 

{

 

if(!AfxOleInit())

 

{

 

AfxMessageBox(“OLE初始化出错!”);

 

return FALSE;

 

}

 

..

 

}

 

函数AfxOleInit在每次应用程序启动时初始化OLE/COM库环境。

 

同DAO和CDatabase一样,ADO由几个接口组成:

 

_ConnectionPtr,_CommandPtr和_RecordsetPtr.

 

不同于DAO和Cdatabase的是,ADO基于COM的接口,因此,假如你没有接触过COM,你
应该在使用ADO前先找有关书籍了解一下COM。

 

3、ADO接口简介

 

ADO库包含三个基本接口:_ConnectionPtr接口、_CommandPtr接口和_RecordsetPtr
接口。

 

_ConnectionPtr接口返回一个记录集或一个空指针。通常使用它来创建一个数据连
接或执行一条不返回任何结果的SQL语句,如一个存储过程。使用_ConnectionPtr
接口返回一个记录集不是一个好的使用方法。通常同CDatabase一样,使用它创建一


个数据连接,然后使用其它对象执行数据输入输出操作。

 

_CommandPtr接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存
储过程和SQL语句。在使用_CommandPtr接口时,你可以利用全局_ConnectionPtr接
口,也可以在_CommandPtr接口里直接使用连接串。如果你只执行一次或几次数据访
问操作,后者是比较好的选择。但如果你要频繁访问数据库,并要返回很多记录集,
那么,你应该使用全局_ConnectionPtr接口创建一个数据连接,然后使用
_CommandPtr接口执行存储过程和SQL语句。

 

_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的
控制功能,如记录锁定,游标控制等。同_CommandPtr接口一样,它不一定要使用一
个已经创建的数据连接,可以用一个连接串代替连接指针赋给_RecordsetPtr的
connection成员变量,让它自己创建数据连接。如果你要使用多个记录集,最好的
方法是同Command对象一样使用已经创建了数据连接的全局_ConnectionPtr接口,然
后使用_RecordsetPtr执行存储过程和SQL语句。

 

4、使用_ConnectionPtr接口

 

_ConnectionPtr是一个连接接口,它类似于CDatabase和CDaoDatabase。它们的工作
原理相似。首先创建一个_ConnectionPtr接口实例,接着指向并打开一个ODBC数据
源或OLE DB数据提供者(Provider)。以下代码和CDaoDatabase分别创建一个基于DSN
和非DSN的数据连接。

 

代码3:使用CDaoDatabase(基于DSN)

 

CDaoDatabase MyDb = new CDaoDatabase();

 

MyDb.Open(NULL,FALSE,FALSE,"ODBC;DSN=samp;UID=admin;PWD=admin");

 

代码4:使用CDaoDatabase(基于非DSN)

 

CDaoDatabase MyDb = new CDaoDatabase();

 

MyDb.Open(NULL,FALSE,FALSE,"ODBC;DRIVER={SQL Server};SERVER=server;

 

DATABASE=samp;UID=admin;PWD=admin");

 

代码5:使用_ConnectionPtr(基于DSN)

 

_ConnectionPtr MyDb;

 


MyDb.CreateInstance(__uuidof(Connection));

 

MyDb->Open("DSN=samp;UID=admin;PWD=admin","","",-1);

 

代码6:使用_ConnectionPtr (基于非DSN)

 

_ConnectionPtr MyDb;

 

MyDb.CreateInstance(__uuidof(Connection));

 

MyDb->Open("Provider=SQLOLEDB;SERVER=server;DATABASE=samp;UID=admin;

 

PWD=admin","","",-1);

 

5、使用_RecordsetPtr接口

 

_RecordsetPtr接口的使用方法和CDaoDatabase类似,通过以下代码的比较,你会发
现使用_RecordsetPtr接口非常简单(以下代码使用上面已经创建的数据连接):

 

代码7:使用CDaoDatabase执行SQL语句

 

CDaoRecordset MySet = new CDaoRecordset(MyDb);

 

MySet->Open(AFX_DAO_USE_DEFAULT_TYPE,"SELECT * FROM t_samp");

 

Now using ADO:

 

代码8:使用_RecordsetPtr执行SQL语句

 

_RecordsetPtr MySet;

 

MySet.CreateInstance(__uuidof(Recordset));

 

MySet->Open("SELECT * FROM some_table",

 

MyDb.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);

 

现在我们已经有了一个数据连接和一个记录集,接下来就可以使用数据了。从以下
代码可以看到,使用ADO的_RecordsetPtr接口,就不需要象DAO那样频繁地使用大而
复杂的数据结构VARIANT,并强制转换各种数据类型了,这也是ADO的优点之一。假
定程序有一个名称为m_List的的ListBox控件,下面代码我们用_RecordsetPtr接口


获取记录集数据并填充这个ListBox控件:

 

代码9:使用DAO访问数据

 

VARIANT * vFieldValue;

 

COleVariant covFieldValue;

 

CString Holder;

 

while(!MySet->IsEOF())

 

{

 

MySet->GetFieldValue("FIELD_1", covFieldValue);

 

vFieldValue = (LPVARIANT)covFieldValue;

 

if(vFieldValue->vt!-VT_NULL)

 

{

 

Holder.Format("%s",vFieldValue->pbVal);

 

m_List.AddString(Holder);

 

}

 

MySet.MoveNext();

 

}

 

代码10:使用ADO访问数据

 

_variant_t Holder

 

try{

 

while(!MySet->adoEOF)

 

{


 

Holder = MySet->GetCollect("FIELD_1");

 

if(Holder.vt!=VT_NULL)

 

m_List.AddString((char*)_bstr_t(Holder));

 

MySet->MoveNext();

 

}

 

}

 

catch(_com_error * e)

 

{

 

CString Error = e->ErrorMessage();

 

AfxMessageBox(e->ErrorMessage());

 

}

 

catch(...)

 

{

 

MessageBox("ADO发生错误!");

 

}

 

必须始终在代码中用try和catch来捕获ADO错误,否则ADO错误会使你的应用程序崩
溃。当ADO发生运行时错误时(如数据库不存在),OLE DB数据提供者将自动创建一个
_com_error对象,并将有关错误信息填充到这个对象的成员变量.

 

6、使用_CommandPtr接口

 

_CommandPtr接口返回一个Recordset对象,并且提供了更多的记录集控制功能,以
下代码示例了使用_CommandPtr接口的方法:

 

代码11:使用_CommandPtr接口获取数据


 

_CommandPtr pCommand;

 

_RecordsetPtr MySet;

 

pCommand.CreateInstance(__uuidof(Command));

 

pCommand->ActiveConnection=MyDb;

 

pCommand->CommandText="select * from some_table";

 

pCommand->CommandType=adCmdText;

 

pCommand->Parameters->Refresh();

 

MySet=pCommand->Execute(NULL,NULL,adCmdUnknown);

 

_variant_t TheValue = MySet->GetCollect("FIELD_1");

 

CString sValue=(char*)_bstr_t(TheValue);

 

7、关于数据类型转换

 

由于COM对象是跨平台的,它使用了一种通用的方法来处理各种类型的数据,因此
CString 类和COM对象是不兼容的,我们需要一组API来转换COM对象和C++类型的数
据。_vatiant_t和_bstr_t就是这样两种对象。它们提供了通用的方法转换COM对象
和C++类型的数据。

 

8、小结

 

数据访问发展的趋势是OLE DB.使用OLE DB最简单的方法是ADO.ADO的对象层次模型
封装了数据库访问细节,为C++程序员提供了一种非常好的数据访问策略。

 

 

 

 

 

 

 

 

 


在Visual C++中用ADO进行数据库编程

作者:蒋东宇

 

 

 

ActiveX数据对象(ADO)是OLE DB上面的高层数据库API。我们在C++程序中也可
以调用ADO。本文将在VC 6.0环境下做一个小小的例子解释如何使用ADO。

 

1. 生成应用程序框架并初始化OLE/COM库环境

 

创建一个标准的MFC AppWizard(exe)应用程序,然后在应用程序类的InitInstance
函数中初始化OLE/COM库(因为ADO库是一个COM DLL库)。

 

BOOL CADOTestApp::InitInstance()

 

{ //初始化OLE/COM库环境

 

AfxOleInit();}

 

2. 引入ADO库文件

 

使用ADO前必须在工程的stdafx.h文件里用直接引入符号#import引入ADO库文件,
以使编译器能正确编译。代码如下:

 

#include 〈comdef.h〉

 

#import "c:\program files\common files\system\ado\msado15.dll"

 

no_namespace

 

rename ("EOF","adoEOF")

 

头文件comdef.h使我们的应用程序能够使用Visual C++中的一些特殊COM支持类,
这些类使得处理OLE自治更为容易一些,OLE自治是ADO使用的数据类型。后三行使用
#import指令在我们的应用程序中输入ADO类库定义。

 

ADO类的定义是作为一种资源存储在ADO DLL(msado15.dll)中,在其内部称为类型
库。类型库描述了自治接口,以及C++使用的COM vtable接口。当使用#import
指令时,在运行时Visual C++需要从ADO DLL中读取这个类型库,并以此创建一组
C++头文件。这些头文件具有.tli 和.tlh扩展名,读者可以在项目的目录下找到
这两个文件。在C++程序代码中调用的ADO类要在这些文件中定义。


 

程序的第三行指示ADO对象不使用名称空间。在有些应用程序中,由于应用程序中的
对象与ADO中的对象之间可能会出现命名冲突,所以有必要使用名称空间。如果要使
用名称空间,则可把第三行程序修改为: rename_namespace("AdoNS")。第四行代
码将ADO中的EOF(文件结束)更名为adoEOF,以避免与定义了自己的EOF的其他库冲
突。

 

3.利用智能指针进行数据库操作

 

在CaboutDlg头文件中定义两个ADO智能指针类实例,并在对话框中加入一个
ListCtrl。

 

_ConnectionPtr m_pConnection;

 

_RecordsetPtr m_pRecordset;

 

ClistCtrl m_List;

 

ADO库包含三个智能指针:_ConnectionPtr、_CommandPtr和_RecordsetPtr。

 

_ConnectionPtr通常被用来创建一个数据连接或执行一条不返回任何结果的SQL语
句,如一个存储过程。

 

_CommandPtr返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过
程和SQL语句。在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也
可以在_CommandPtr接口里直接使用连接串。

 

_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的
控制功能,如记录锁定、游标控制等。

 

在OnInitDialog()中加入以下代码:

 

BOOL CAboutDlg::OnInitDialog()

 

{

 

CDialog::OnInitDialog();

 

_variant_t TheValue;

 

m_List.ResetContent();


 

m_pConnection.CreateInstance(_uuidof(Connection));

 

m_pRecordset.CreateInstance(_uuidof(Recordset));

 

try{

 

m_pConnection->Open("DSN=ADOTest","","",0); //连接叫作ADOTest的ODBC数据

 

m_pRecordset->Open("SELECT * FROM BlockDefine",(IDispatch*)m_pConnection,

adOpenDynamic,

adLockOptimistic,

adCmdText);

 

//执行SQL语句得到一个记录集

 

while(!m_pRecordset->adoEOF)

 

//遍历所有记录

 

{

 

TheValue = m_pRecordset->GetCollect("BlockIndex");

 

//得到字段BlockIndex的值

 

if(TheValue.vt!=VT_NULL)

 

m_List.AddString((char*)_bstr_t(TheValue)); //将该值加入到列表控件中

 

m_pRecordset->MoveNext();

 

}

 

m_pRecordset->Close();

 

m_pConnection->Close();

 

}

 


catch(_com_error e) //异常处理

 

{

 

AfxMessageBox(e->ErrorMessage());

 

}

 

m_pRecordset = NULL;

 

m_pConnection = NULL;

 

return TRUE; // return TRUE unless you set the focus to a control

 

}

 

程序中通过_variant_t和_bstr_t转换COM对象和C++类型的数据, _variant_t类
封装了OLE自治VARIANT数据类型。在C++中使用_variant_t类要比直接使用
VARIANT数据类型容易得多。

 

好,编译后该程序就能运行了,但记住运行前要创建一个叫ADOTest的ODBC数据源。
该程序将把表BlockDefine中的BlockIndex字段值显示在列表控件中。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


VC++ ADO开发实践之七 zswzwy(收藏)

关键字

 ADO

 

一致的数据访问技术ADO/OLE DB

 

 Microsoft新近推出的UDA(Universal Data Access,一致数据访

问技术)为关系型或非关系型数据访问提供了一致的访问接口,为企业

级Intranet应用多层软件结构提供了数据接口标准。一致数据访问包

括两层软件接口,分别为ADO(Active Data Object)和OLED B,对应于

不同层次的应用开发,ADO提供了高层软件接口,可在各种脚本语言(Sc

ript)或一些宏语言中直接使用;OLE DB提供了底层软件接口,可在C/C

++语言中直接使用。ADO以OL E DB为基础,它对OLE DB进行了封装。

一致数据访问技术建立在Microsoft的COM(组件对象模型)基础上,它

包括一组COM组件程序,组件与组件之间或者组件与客户程序之间通过

标准的COM接口进行通讯。

 由于ADO建立在自动化(Automation)基础上,所以ADO的应用场合

非常广泛,不仅可在Visual Basic这样的高级语言开发环境中使用,还

可以在一些脚本语言中使用,这对于开发Web应用,在ASP(Active Serv

er Page)的脚本代码访问数据库中提供了操作应用的捷径。本文将首

先介绍ADO和OLE DB的结构模型,以便读者了解ADO与OLE DB之间的关

系,然后介绍ADO的对象模型和ADO的一些特性,同时我们也将通过一些

例子代码向读者展示ADO的用法。

 一、一致数据访问介绍

 随着网络技术和数据库技术的不断发展,现在的应用系统对数据

集成的要求越来越高,这些数据有可能分布在不同的地方,并且使用不

同的格式,例如关系型数据库和操作系统中的文件、电子表格、电子

邮件、多媒体数据以及目录服务信息等等。传统的解决方案是使用大

型的数据库系统,把所有这些数据都移到数据库系统中,然后按照操作

数据库的办法对这些数据进行访问,这样做虽然能够按统一的方式对

数据进行各种操作,但这种间接访问方式带来了很多问题,比如数据更

新不及时、空间资源的冗余和访问效率低等等。

 Microsoft公司推出的一致数据访问技术则较好地解决了这些问

题,它使得应用通过一致的接口来访问各种各样的数据,而不管数据驻

留在何处,也不需要进行数据转移或复制、转换,在实现分布式的同时

也带来了高效率。并且UDA技术在统一数据访问接口的同时,它的多层

结构使数据使用方有了更多的选择机会,而它强大的扩展能力也给数

据提供方留下了更多的扩展余地,这种开放型的软件结构使它具有极

强的生命力,所以,这种技术从一推出便获得了广泛的欢迎,可以说,UD

A技术是继ODBC之后的又一数据访问技术的飞跃。

 UDA技术包括OLE DB和ADO两层标准接口,OLE DB是系统级的编程


接口,它定义了一组COM接口,这组接口封装了各种数据系统的访问操

作,这组接口为数据使用方和数据提供方建立了标准,OLE DB还提供了

一组标准的服务组件,用于提供查询、缓存、数据更新、事务处理等

操作,因此,数据提供方只需实现一些简单的数据操作,在使用方就可

以获得全部的数据控制能力。

 ADO是应用层的编程接口,它通过OLE DB提供的COM接口访问数据,

它适合于各种客户机/服务器应用系统和基于Web的应用,尤其在一些

脚本语言中访问数据库操作是ADO的主要优势。ADO是一套用自动化技

术建立起来的对象层次结构,它比其他的一些对象模型如DAO(Data Ac

cess Object)、RDO(Remote Data Object)等具有更好的灵活性,使用

更为方便,并且访问数据的效率更高。

 图1显示了统一数据访问的软件层次模型。

图1 UDA的层次结构图

 从图中我们可以看出,应用程序既可以通过ADO访问数据也可以直

接通过OLE DB访问数据,而ADO则通过OLE DB访问底层数据。而且,OLE

 DB分成两部分,一部分由数据提供者实现,包括一些基本功能,如获取

数据、修改数据、添加数据项等;另一部分由系统提供, 包括一些高

级服务,如游标功能、分布式查询等等。这样的层次结构既为数据使

用者即应用程序提供了多种选择方案,又为数据提供方简化了服务功

能的实现手段,它只需按OLED B规范编写一个COM组件程序即可,使得

第三方发布数据更为简便,而在应用程序方可以得到全面的功能服务,

这充分体现了OLE DB两层结构的优势。

 由于OLE DB和ADO都以COM组件的形式实现,所以COM组件的各种特

性也使得构建数据应用更为灵活,而不仅仅局限于一般的客户机/服务

器或Web应用模型,它既适合于以数据为中心的应用,也适合于多层结

构的分布式软件系统。通过对COM组件的配置,我们可以建立各种复杂

的应用系统。利用从COM到DCOM的位置透明技术,我们可以很方便地建

立分布式应用系统;利用MTS(Microsoft Transaction Server)运行环

境,我们也可以在数据访问一层增加安全性控制,并利用MTS的对象管

理使数据访问效率更高。所有这些功能都无需数据提供方编写代码实

现,只需在DCOM或MTS环境中进行常规的配置即可。

 可以说一致的数据访问技术的核心是OLE DB,OLE DB建立了数据

访问的标准接口,它把所有的数据源经过抽象形成行集(rowset)的概

念。OLE DB模型主要包括以下一些COM对象:

 (1)数据源(Data Source)对象 数据源对象对应于一个数据提供

者,它负责管理用户权限、建立与数据源的连接等初始操作。

 (2)会话(Session)对象 在数据源连接的基础上建立会话对象,会

话对象提供了事务控制机制。

 (3)命令(Command)对象 数据使用者利用命令对象执行各种数据

操作,如查询命令、修改命令等。

 (4)行集(Rowset)对象 提供了数据的抽象表示,它可以是命令执


行的结果,也可以直接由会话对象产生,它是应用程序主要的操作对象

 OLE DB的对象模型非常简单,这种简单性也带来了灵活性,从上面

的几个COM对象也可以看出这一点。下面我们将从应用层角度出发,通

过建立在OLE DB基础上的ADO对象模型结构的分析和使用以帮助读者

进一步理解一致数据访问技术。

 二、ADO对象模型

 ADO对象模型定义了一组可编程的自动化对象,可用于Visual Bas

ic、Visual C++、Java以及其他各种支持自动化特性的脚本语言。AD

O最早被用于Microsoft Internet In formation Server中访问数据

库的接口,与一般的数据库接口相比,ADO可更好地用于网络环境,通过

优化技术,它尽可能地降低网络流量;ADO的另一个特性是使用简单,不

仅因为它是一个面向高级用户的数据库接口,更因为它使用了一组简

化的接口用以处理各种数据源。这两个特性使得ADO必将取代RDO和DA

O,成为最终的应用层数据接口标准。

 从图1我们也看到了ADO实际上是OLE DB的应用层接口,这种结构

也为一致的数据访问接口提供了很好的扩展性,而不再局限于特定的

数据源,因此,ADO可以处理各种OLE DB支持的数据源。

 图2是ADO的对象模型图。

图2 ADO对象模型

 在ADO模型中,主体对象只有3个:Connection、Command和Records

et,其他4个集合对象Errors、Properties、Parameters和Fields分别

对应Error、Property、Parameter和Field对象,整个ADO对象模型由

这些对象组成。

 一个典型的ADO应用使用Connection对象建立与数据源的连接,然

后用一个Command对象给出对数据库操作的命令,比如查询或者更新数

据等,而Recordset用于对结果集数据进行维护或者浏览等操作。Comm

and命令所使用的命令语言与底层所对应的OLE DB数据源有关,不同的

数据源可以使用不同的命令语言,对于关系型数据库,通常使用SQL作

为命令语言。

 在Connection、Command和Recordset 3个对象中,Command对象是

个可选对象,它是否有效取决于OLE DB数据提供者是否实现了IComman

d接口。由于OLE DB可提供关系型数据源也可以提供非关系型数据源,

所以在非关系型数据源上使用传统的SQL命令查询数据有可能无效,甚

至Command命令对象也不能使用。

 从结构上看,ADO模型非常简单,但使用上又非常灵活,下面我们先

从单个对象的角度进行讨论:

 (1) Connection对象 Connection对象代表与数据源之间的一个

连接,ADO的Connec tion对象封装了OLE DB的数据源对象和会话对象

。根据OLE DB提供者的不同性能,Conne ction对象的特性也有所不同

,所以Connection对象的方法和属性不一定都可以使用。利用Connect


ion对象,我们可以完成以下一些基本设置操作。

 a.通过ConnectionString、ConnectionTimeOut和Mode属性设置

连接串、超时信息、访问模式。

 b.还可以设置CursorLocation属性以便指定使用客户端游标,以

便在客户程序中使用批处理修改方式。

 c.设置连接的缺省数据库属性DefaultDatabase。

 d.设置OLE DB提供者的属性Provider。

 e.通过Open和Close控制Connection对象与物理数据源的连接。

 f.通过Execute方法执行命令。

 g.提供事务机制,通过BeginTrans、CommitTrans和RollbackTran

s方法实现事务控制。

 h.通过Errors集合属性检查数据源的错误信息。

 i.通过OpenSchema方法获取数据库的表信息。

 Connection对象是ADO的基本对象之一,它独立于所有其他的对象

。如果我们要对数据库进行查询操作,既可以使用Execute方法,也可

以使用Command对象。使用Execute方法比较简便,但用Command对象可

以保存命令的信息,以便多次查询。

 (2) Command对象 Command对象代表一个命令,可以通过其方法执

行针对数据源的有关操作,比如查询、修改等。Command对象的用法如

下:

 a.通过CommandText属性设置命令串。

 b.通过Parameters集合属性和Parameter对象定义参数化查询或

存储过程的参数。

 c.通过Execute方法执行命令,可能的话,返回Recordset对象。

 d.在执行命令之前,可通过设置CommandType属性以便优化性能。

 e.可以通过Prepared属性指示底层的提供者为当前命令准备一个

编译过的版本,以后再执行时,速度会大大加快。

 f.通过CommandTimeOut属性设置命令执行的超时值(以秒为单位)

 g.可以设置ActiveConnection属性,为命令指定连接串,Command

对象将在内部创建C onnection对象。

 h.可以设置Name属性,这样以后可以在相应的Connection对象上

按Name属性指定的方法名执行。

 Command对象执行时,既可以通过ActiveConnection属性指定相连

的Connection对象,也可以独立于Connection对象,直接指定连接串,

即使连接串与Connection对象的连接串相同,Command对象仍然使用其

内部的数据源连接。

(3)Recordset对象 Recordset对象代表一个表的记录集或者命令

执行的结果,在记录集中,总是有一个当前的记录。记录集是ADO管理

数据的基本对象,所有的Recordset对象都按照行列方式的表状结构进

行管理,每一行对应一个记录(Record),每一列对应一个域(Field)。


Recordset对象也通过游标对记录进行访问,在ADO中,游标分为以下4种

:

 静态游标提供对数据集的一个静态拷贝,允许各种移动操作,包括

前移、后移等等,但其他用户所做的操作反映不出来。

 动态游标允许各种移动操作,包括前移、后移等等,并且其他用户

所做的操作也可以直接反映出来。

 前向游标允许各种前向移动操作,不能向后移动,并且其他用户所

做的操作也可以直接反映出来。

 键集(keyset)游标 类似于动态游标,也能够看到其他用户所做的

数据修改,但不能看到其他用户新加的记录,也不能访问其他用户删除

的记录。

 Recordset对象的用法如下:

 a.利用CursorType属性设置游标类型。

 b.通过Open方法打开记录集数据,既可以在Open之前对ActiveCon

nection属性赋值, 指定Recordset对象使用连接对象,也可以直接在O

pen方法中指定连接串参数,ADO将创建一个内部连接,即使连接串与外

部的连接对象相同,它也使用新的连接对象。

 c.Recordset对象刚打开时,当前记录被定位在首条记录,并且BOF

和EOF标志属性为F alse,如果当前记录集为空记录集,则BOF和EOF标

志属性为True。

 d.通过MoveFirst、MoveLast、MoveNext和MovePrevious方法可

以对记录集的游标进行移动操作。如果OLE DB提供者支持相关功能的

话,可以使用AbsolutePosition、Absol utePage和Filter属性对当前

记录重新定位。

 e.ADO提供了两种记录修改方式:立即修改和批修改。在立即修改

方式下,一旦调用U pdate方法,则所有对数据的修改立即被写到底层

的数据源。在批修改方式下,可以对多条记录进行修改,然后调用Upda

teBatch方法把所有的修改递交到底层数据源。递交之后,可以用Stat

us属性检查数据冲突。

 Recordset对象是ADO数据操作的核心,它既可以作为Connection

对象或Command对象执行特定方法的结果数据集,也可以独立于这两个

对象而使用,由此可以看出ADO对象在使用上的灵活性。

 上面3个对象都包含一个Property对象集合的属性,通过Property

对象可使ADO动态暴露出底层OLE DB提供者的性能。由于并不是所有

的底层提供者都有同样的性能,所以ADO 允许用户动态访问底层提供

者的能力。这样既使得ADO很灵活,又提供了很好的扩展性。

 ADO的其他集合对象及其元素对象,都用在特定的上下文环境中,

比如Parameter对象一定要与某个Command对象相联系后,才能真正起

作用。而另外三个对象Field、Error和Property对象只能依附于其父

对象,不能单独创建这些对象。

 三、在多种语言中使用ADO


 以上介绍了ADO的对象模型,现在我们来讨论如何在不同的语言环

境中使用ADO对象。

 因为ADO是作为自动化组件程序实现的,所以我们可以在任何支持

COM和自动化特性的语言环境中使用ADO,比如Visual Basic、Visual

C++、ASP和Java等等,下面分别加以介绍。

 1. 在Visual Basic应用中使用ADO

 Visual Basic应用在设计模式和运行模式下都可以创建和使用自

动化对象,在设计模式下,像ADO这样的对象库可以作为内部对象来使

用,我们只需在"Project"菜单下的"Ref erences"命令弹出的对话框

中选中ADO对象库"Microsoft ActiveX Data Objects Libra ry",于

是我们就可以在程序中直接声明或新建ADO对象,举例如下:

 Dim cn as New ADODB.Connection

 Dim cmd as New ADODB.Command

 Dim rs as New ADODB.Recordset

 可以看出,在设计时使用ADO对象非常方便,而且Visual Basic设

计环境中提供的对象浏览器(Object Browser)功能允许用户很方便地

查看ADO对象的属性和方法。

 我们也可以在运行时创建自动化对象,使用Visual Basic的Creat

eObject函数可以创建任意的自动化对象,由于ADO中只有Connection

对象、Command对象和Recordset对象可以被独立创建,所以我们也只

能创建这3种对象,举例如下:

 Dim cn

 Set rs=CreateObject("ADODB.Connection ")

 Dim cmd

 Set rs=CreateObject("ADODB.Command")

 Dim rs

 Set rs=CreateObject("ADODB.Recordset")

 不管是设计模式还是运行模式,调用ADO对象的属性和方法都非常

简单,直接调用即可。

 2. 在ASP的VBScript中使用ADO

 ADO对象也可以用于HTML和Active Server Page的VBScript脚本

代码,VBScript脚本代码与Visual Basic的代码很类似,它们内嵌在HT

ML或ASP文件的特定标记对内部。但VB Script引擎比Visual Basic的

设计环境或运行库在功能上还是要弱一些,首先,在VBScri pt代码中,

没有与设计环境类似的用法,VBScript引擎不能装入ADO类型库,所以

不能使用New操作符创建ADO对象,但可以使用CreateObject函数创建

对象;其次,ADO对象库中用到的常量只能通过包含文件引入,随ADO一

起提供的Adovbs.inc文件包含所有ADO常量的定义,我们可在脚本代码

中直接包含此文件。

 因此,为了在VBScript代码中使用ADO,首先要包含Adovbs.inc文

件,然后使用Create Object函数创建ADO对象,以后就可以调用这些对


象的属性或方法了。下面的例子显示了在ASP文件中用ADO列出数据表

中所有作者的姓名和职称,代码如下:

 Using ADO in a Visual Basic Script Web Page

 Using ADO in a Visual Basic Script

 Web Page

 myConnection.Open "DSN=MySamples;UID=sa"

 SQLQuery = "select AuthorName, Title from AuthorDB"

 set rs = myConnection.Execute(SQLQuery)%

 WIDTH=200

 Title ID

 SIZE=1

 Title

 "arial narrow" size=1

 "arial narrow" size=1

 3. 在Visual C++中使用ADO

 在Visual C++中使用ADO有多种方法,第一种方法是我们使用CoCr

eateInstance函数创建ADO对象,并得到对象的IDispatch接口指针,然

后调用其Invoke函数,用这种方法需要我们自己处理参数和返回值,AD

O提供了Adoid.h和Adoint.h头文件分别定义了ADO对象的CLSID和接口

ID;第二种方法是利用#import编译指示符(在Visual C++ 5.0及以后

的版本中可以使用),可以方便地使用ADO对象;第三种方法是利用MFC(

Microsoft Foundation C lass)库提供的IDispatch接口封装类COleD

ispatchDriver创建和调用ADO对象。

 下面的代码显示了在Visual C++创建数据源连接的过程:

 GUID connectionCLSID;

 HRESULT hResult = ::CLSIDFromProgID(L"ADODB.Connection",

 &connectionCLSID);

 if (FAILED(hResult))

 {

 ......

 }

 IDispatch *pDispatch = NULL;

 hResult = CoCreateInstance(connectionCLSID, NULL,

 CLSCTX_SERVER, IID_IDispatch, (void **)&pDispatch);

 if (FAILED(hResult))

 {

 ......

 }

 COleDispatchDriver driver;

 driver.AttachDispatch(pDispatch, FALSE);

 TRY


 {

 BYTE parms =VTS_BSTR;

 driver.InvokeHelper(0xa, DISPATCH_METHOD,

 VT_EMPTY, &hResult, parms,L"Provider=SQLOLEDB;

 User ID=sa;Password=;" L"Initial Catalog=LEAVES;

 Data Source=NetTestServer");

 }

 END_TRY

 driver.DetachDispatch();

 ......

 4. 在Java中使用ADO

 在Java程序中可以引入ADODB类,然后声明ADO变量,也可以使用ne

w操作符创建ADO变量。下面的代码说明了如何在Java中打开与数据源

连接:

 import msado10.*;

 _Connection m_conn = null;

 _Recordset m_rs = null;

 _Command m_cmd = null;

 void OpenConnection()

 {

 String s;

 Properties properties;

 try

 {

 properties = m_conn.getProperties();

 m_conn.Open("dsn=MySamples", "sa", "");

 properties = null;

 }

 catch (Exception e)

 { System.out.println("\nUnable to make a connection \n")

;

 }

 }

 


你可能感兴趣的:(数据库,Microsoft,command,null,mfc,VBScript)