您正在看的VC教程是:VC数据库编程技术应用。ADO C++ Extensions
实际上,如果你是用C++进行应用程序开发的话,就应该使用ADO C++ Extensions。我们知道,在VB或者用VBScript来操作ADO的话,是非常方便的,但是如果使用C/C++或者是Java的话,就必须要处理类似VARIANTs这样的数据结构,实现和C++数据结构的转换,而这个无疑是所有C++开发人员都很头疼的事情。但如果你使用C++扩展的话, ADO不需要从数据提供者处得到列信息。ADO在设计时刻使用开发人员提供的列信息。以下是一个简单的示例:
file://创建和具体记录相对应的类
class CAuthor : public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs1)
ADO_VARIABLE_LENGTH_ENTRY4(1, adVarChar, m_szau_id, sizeof(m_szau_id), FALSE)
ADO_VARIABLE_LENGTH_ENTRY4(2,adVarChar,m_szau_fname,sizeof(m_szau_fname), FALSE)
ADO_VARIABLE_LENGTH_ENTRY4(3,adVarChar,m_szau_lname,sizeof(m_szau_lname), FALSE)
END_ADO_BINDING()
protected:
char m_szau_id[12];
char m_szau_fname[21];
char m_szau_lname[41];
};
void FetchAuthorData()
{
CAuthor author;
_RecordsetPtr pRs; file://记录集对象
IADORecordBinding *piAdoRecordBinding;
pRs.CreateInstance(__uuidof(Recordset)); file://获取COM对象接口指针
file://得到需要的记录集
pRs->Open("select au_id, au_fname, au_lname from Employees",
"Provider=SQLOLEDB;Data Source=sureshk1;Database=pubs;
User Id=sa;Password=;",
adOpenForwardOnly, adLockReadOnly, adCmdText);
file://查询接口IADORecordBinding
pRs->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&piAdoRecordBinding);
file://和我们自己定义的对象相绑定
piAdoRecordBinding->BindToRecordset(&author);
while (VARIANT_FALSE == pRs->EOF) file://得到记录中的相关内容
{
printf("%s %s %s", author.m_szau_id, author.m_szau_fname, author.m_szau_lname);
pRs->MoveNext();
}
piAdoRecordBinding->Release(); file://释放对象
}
OLE DB是一个标准,它提供了一系列数据接口,通过这些接口我们可以操作各种不同的数据源,并能够进行无缝的集成,这些接口包含了到达数据和操作数据的工业标准。开发人员可以使用这些接口来定义非常简单的数据提供者,当然也可以提供非常复杂的象关系数据库这样的操作。它是建立在微软基于组件计算的策略上的。
因为OLE DB标准在各个层次定义了数据库操作的模式,使用OLE DB的组件可以对数据库进行非常复杂的操作,并且在多层应用系统开发领域大有作为。
建立在OLE DB基础上的三层组件系统说明:
数据提供者
实际上,OLD DB提供了一个数据提供者这个组件用来实现对所有不同的数据源(包括关系数据库、日志文件和非常复杂的格式比如IMS,ADABAS或者是电子邮件)的统一的操作,其结果表现形式为所有的信息都是以行集的形式返回给应用程序。数据提供者组件使数据源的厂商可以通过暴露行集的方法来实现接口。
数据消费者:
OLE DB数据消费者表示那些需要和数据源进行交互的系统或者应用,包括开发工具和个人产品工具等。
数据服务提供者
数据服务提供者目的是用来开发数据库组件,比如查询处理器或者是游标引擎,作为一个独立的产品能够无缝的和存在的基于OLE DB的数据提供者进行集成。
OLE DB数据提供者是以DLL形式存在的,所以在具体应用的时候,它就被装入应用程序所在的空间中,也就意味着OLE DB接口只能在进程范围内工作。数据提供者可以直接和数据源进行交互,也可以通过IPC进行交互。
OLE DB消费者(Consumer)模板是一个类的集合,它用来帮助开发人员方便的进行基于OLE DB的应用程序的开发。OLE DB消费者模板是以一种和ATL相似的模板类库的形式提供给开发人员的,在模板类库中的类可以被分成两大类:提供者类(Provider Class)和消费者类(Consumer Class),OLE DB提供者模板支持OLE DB2标准,采用这些模板,开发人员可以实现简单的只读类型的提供者应用程序。OLE DB消费者模板简化了消费者应用程序的设计。这些模板提供了对ATL和MFC的支持,并支持C++的数据类型,同时,可以方便对数据表字段进行绑定,可以执行参数化的查询来提取记录集,它提供的类主要有以下这些:
CDataSource: 用来和OLE DB中的数据源对象相对应
CEnumerator:调用了OLE DB根Enumerator对象
CSession: 表示和一个数据库的会话,一个数据源可以存在一个或者多个会话
CAccessorRowset
CTable
CCommand
这里我们分析一下用消费者模板创建应用的步骤:
打开一个数据源:创建CDatasource,CSession,CDBPropset变量,其中CDatasource变量表示数据源对象, CSession变量维护一个和数据源的会话过程,CDBPropset变量帮助定义属性,使它们能够传递给OLE DB的模板函数。基本代码如下:
HRESULT hr;
CDatasource db;
CDBPropSet dbinit(DBPROPSET_DBINIT);
CSession session;
注意如果你需要使用OLE DB消费者模板类的话,需要添加头文件"atldbcli.h",同时需要注意的是ATL OLE DB消费者模板不需要全部的ATL支持,比如你可以在基于工作台(console)的工程中使用ATL OLE DB消费者类,而不需要一个全局的CcomModule对象。
对CDBPropSet变量添加连接属性,连接属性包括UID、password、数据库名称和服务器位置。比如下面的代码表示在一台名称为TTSERVER01的服务器上打开一个名为TTMIS的数据库,用户名称为sa,密码为admin.
DbInit.AddProperty(DBPROP_AUTH_PASSWORD,L"admin");
DbInit.AddProperty(DBPROP_AUTH_USERID,L"sa");
DbInit.AddProperty(DBPROP_INIT_CATLOG,L"TTMIS");
DbInit.AddProperty(DBPROP_INIT_DATASOURCE,L"TTSERVER01");
对CDataSource 调用方法Open,注意如果方法Open不带参数的话,系统将打开数据链接属性对话框让用户选择数据提供者和数据源。实际上我们可以用方法 OpenWithServiceComponents方法,它将激活资源缓冲池服务,同时打开数据源。下面的代码表示使用在CDBPropSet变量中指定的参数打开数据源:
file://SQLOLEDB是SQL SERVER服务器的原始的数据提供者。
hr=db.OpenWithServiceComponent("SQLOLEDB.1",&dbInit");
调用CSession类中的方法Open开始和数据源的一个对话,代码如下:
hr=session.Open(db)
下面就开始讨论如何获取数据集的问题:
在你要提取数据集之前,你必须定义你怎样到达数据集,并且你要提取的数据集的类型是什么,然后你需要添加数据集的属性,下面是添加数据集属性的示例代码:
CDBPropSet propset(DBPROPSET_ROWSET);
file://表示记录集是否可以向后滚动
Propset.AddProperty(DBPROP_CANFETCHBACKWARD,TRUE);
Propset.AddProperty(DBPROPSET_IRowsetScroll,TRUE);
Propset.AddProperty(DBPROPSET_IRowsetChange,TRUE);
Propset.AddProperty(DBPROPSET_UPDATABILITY,DBPROPVAL_UP_CHANGE|DBPROPVAL_UP_DELETE);
根据数据提供者的类型,OLE DB使用这些属性来创建一个服务器端的游标。
通过调用类Ccomand或者是Ctable的方法Open来提取记录集,下面的代码表示你能够通过执行一条简单的Select语句来得到记录集
hr=cmd.Open(session,"Select CustomerID from customers",&propset);
你也可以通过Ccommand对象来执行一条命令,比如一个SQL命令或者是一个参数化的查询。
下面是一个相对完整的例子说明如何通过使用OLE DB消费者模板建立一个基于参数的查询结果集的获取,为了运行这个例子,你需要拷贝test.cpp到你的console工程上去。本例子使用了SQL SERVER自带的pub数据库。
file://test.cpp
#include // OLE DB消费者模板所需要的头文件
#include
#define RETURNHR(hr) if(FAILED((HRESULT)hr))
{
AtlTraceErrorRecords((HRESULT)hr);
return E_FAIL;
}
class CAuthors
{
public:
file://对应Authors表中的字段
TCHAR m_au_id[11];
TCHAR m_au_lname[40];
TCHAR m_au_fname[20];
file://对应的参数变量
TCHAR m_inParam[20];
file://下面的宏是OLE DB消费者模板已经定义的
BEGIN_PARAM_MAP(CAuthors)
SET_PARAM_TYPE(DBPARAMIO_INPUT)
COLUMN_ENTRY(1, m_inParam)
END_PARAM_MAP()
BEGIN_COLUMN_MAP(CAuthors)
COLUMN_ENTRY(1, m_au_id)
COLUMN_ENTRY(2, m_au_lname)
COLUMN_ENTRY(3, m_au_fname)
_COLUMN_MAP()
};
int main(void)
{
CoInitialize(NULL); file://初始化COM对象
HRESULT hr;
CDataSource connection;
CSession session;
CCommand
CDBPropSet dbinit(DBPROPSET_DBINIT);
file://数据连接属性设置
dbinit.AddProperty(DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO, false);
dbinit.AddProperty(DBPROP_AUTH_USERID, OLESTR("sa"));
dbinit.AddProperty(DBPROP_INIT_DATASOURCE, OLESTR("TTMIS"));
dbinit.AddProperty(DBPROP_INIT_PROMPT, (short)4);
dbinit.AddProperty(DBPROP_INIT_CATALOG, OLESTR("pubs"));
hr = connection.Open(_T("SQLOLEDB"), &dbinit);
CDBPropSet propset(DBPROPSET_ROWSET);
file://数据集合属性设置
file://表示可以对返回的结果集进行update,insert和操作
propset.AddProperty(DBPROP_IRowsetChange, true);
propset.AddProperty(DBPROP_UPDATABILITY,
DBPROPVAL_UP_CHANGE|DBPROPVAL_UP_INSERT|
DBPROPVAL_UP_DELETE);
file://开启一个会话过程
RETURNHR(hr = session.Open(connection))
file://创建命令
RETURNHR(hr = authors.Create(session, _T("select au_id, au_lname, au_fname "
"from dbo.authors where au_fname = ?") ))
file://执行次数设定
RETURNHR(hr = authors.Prepare(3))
file://填写参数
_tcscpy(authors.m_inParam, "Tom");
file://第一次执行,注意如果对返回的结果集不做任何改动的话,
file://可以用author.Open()完成我们的目的
RETURNHR(hr = authors.Open(&propset))
file://数据集合提取
while( authors.MoveNext() == S_OK)
{
cout<
file://第二次执行
RETURNHR(hr = authors.Open(&propset))
file://对结果集合进行操作,具体代码略
authors.Close();
file://第三次执行
file://...
file://关闭会话
session.Close();
file://关闭连接
connection.Close();
return S_OK;
}
这里需要注意的是即使你在每次执行后用了命令CCommand::Close以后,命令的参数accessor仍然处于活动状态,命令是在服务器端执行的,这样做的目的是为了提高多次执行的效率。实际少你也可以通过CCommand::Execute进行多次执行,而不是采用CCommand::Open和 CCcommand::Prepare这样的形式。但是使用后者显得更加容易和简洁。
评论