VC++ 实战OLEDB编程(三)

发布时间:2011年03月24日      浏览次数:1309 次
接下来我们详细的讨论有关命令(Command)对象的各个接口。通过前面的两篇文章,大家应该已经知道一些基础知识:属性集合,属性,对象,接口,如何打开连接,如何创建事务等等,有了这些基础的概念性的知识,对于理解和应用好OLEDB编程接口是非常重要的,对于访问数据源数据的任务来说前面仅仅是开始。对于一个像数据库这类的数据源,操作它最好的方法就是使用SQL语句,在OLEDB中对执行SQL语句提供了完整的支持,实现这一功能最重要的对象就是Command,首先我们来看看这个对象的接口全貌:
CoType TCommand
{
[mandatory] interface IAccessor;
[mandatory] interface IColumnsInfo;
[mandatory] interface ICommand;
[mandatory] interface ICommandProperties;
[mandatory] interface ICommandText;
[mandatory] interface IConvertType;
[optional] interface IColumnsRowset;
[optional] interface ICommandPersist;
[optional] interface ICommandPrepare;
[optional] interface ICommandWithParameters;
[optional] interface ISupportErrorInfo;
[optional] interface ICommandStream;
}
现在只要一看到这张表,应该立刻就清楚其中哪些接口是强制的,哪些是可选的。通常我们使用IDBCreateCommand事务接口来创建Command对象,当然这个事务对象可以直接从IDBCreateSession接口创建得来,下面就让我们看一个相对完整的例子,看看如何从连接数据库开始,直到创建出一个Command对象(这是一个非常繁琐的过程,记住每一步是非常重要的):
//1、一大堆头文件和定义,作为前面例子代码的一个相对完整的总结
#define COM_NO_WINDOWS_H //如果已经包含了Windows.h或不使用其他Windows
//库函数时
#define DBINITCONSTANTS
#define INITGUID
#define OLEDBVER 0x0250
#include "oledb.h" // OLE DB Header
#include "oledberr.h" // OLE DB Errors
#include "msdasc.h" // OLE DB Service Component header
#include "msdaguid.h" // OLE DB Root Enumerator
#include "msdasql.h" // MSDASQL - Default provider
int main()
{
CoInitialize(NULL);
//2、创建IDataInitialize接口
IDataInitialize* pIDataInitialize = NULL;
CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
IID_IDataInitialize,(void**)&pIDataInitialize);
IDBProperties *pIDBProperties = NULL;
IDBInitialize *pIDBInitialize = NULL;
//2、直接创建IDBProperties接口
pIDataInitialize->CreateDBInstance(CLSID_MSDASQL, NULL,
CLSCTX_INPROC_SERVER, NULL, IID_IDBProperties,
(IUnknown**)&pIDBProperties);
//3、定义属性集合,属性(略),设置属性
......
//4、连接到数据源
pIDBProperties->QueryInterface(IID_IDBInitialize,(void**)&pIDBInitialize);
pIDBInitialize->Initialize();
//5、得到事务对象
IDBCreateSession* pIDBCreateSession = NULL;
pIDBInitialize->QueryInterface(IID_IDBCreateSession,(void**)& pIDBCreateSession);
//注意这次直接创建了IDBCreateCommand对象作为事务对象,注意一般情况下
//因为这个接口是个可选接口,所以直接创建有可能会失败,所以要检验返回值
//在这一系列文章中我省略这些检验性的操作,在实际的代码中一定要包含丰富的
//错误处理代码,有关错误处理的话题我将在以后的专门文章中详细阐述,这里先//聚焦于我们所关心的问题
IDBCreateCommand* pIDBCreateCommand = NULL;
pIDBCreateSession->CreateSession(NULL,IID_IDBCreateCommand,
(IUnknown**)&pIDBCreateCommand);
//6、创建Command对象,我们直接创建ICommandText接口
ICommandText* pICommandText = NULL;
pIDBCreateCommand->CreateCommand(NULL,IID_ICommandText,
(IUnknown**)&pICommandText);
......
}
上面例子的最后一步我们通过创建一个ICommandText接口来创建了一个Command对象,在通常的例子中是通过创建ICommand接口来创建一个Command对象,因为他们是等价的,此处例子中之所以没有这样做,就是为了加深大家对COM对象和COM接口概念的理解,而这是彻底掌握和理解OLEDB接口的基础。
创建Command对象的一般目的就是为了能够执行一段SQL语句,注意是一段SQL语句而不是一句,此处说的一段的含义是,既可以一次执行一句,也可以一次执行多句,甚至是多句Select语句。后面的讨论中还将继续深入讨论这个话题,这里我们先来看看ICommandText接口,注意在Command对象中ICommandText接口实际是从ICommand接口派生来的,因此ICommandText接口实际包含了ICommand接口的全部方法:
HRESULT SetCommandText(REFGUID rguidDialect,LPCOLESTR pwszCommand);
HRESULT GetCommandText(GUID* pguidDialect,LPOLESTR* ppwszCommand);
//以下的方法实际也是ICommand的方法:
HRESULT Cancel();
HRESULT Execute(IUnknown* pUnkOuter,REFIID riid,DBPARAMS* pParams,
DBROWCOUNT *pcRowsAffected,IUnknown** ppRowset);
HRESULT GetDBSession(REFIID riid,IUnknown** ppSession);
在这些方法中我们实际经常用到的就是SetCommandText和Execute方法。在SetCommandText方法中有一个奇怪的参数rguidDialect,这参数其实就是为了指定各种SQL方言而定的,因为我们知道SQL Server 的T-SQL语句和ORACLE的P-SQL语句很多细节的语法上是不兼容的,因此不论你怎么使用OLEDB接口,都必须了解你所连接的数据库支持的SQL语言的一些细节语法(SQL方言),虽然各大数据库厂商都声称自己的SQL语句是如何符合SQL语言标准的,但是遗憾的是,它们之间的细节是非常不同的,所以OLEDB接口在这里是无能为力的,你必须学好SQL语言。而OLEDB只不过就是个编程接口而已。
通常情况下,我们不必刻意的去设置一个专有的“SQL方言”的GUID,而是直接指定DBGUID_DEFAULT常量即可,剩下的语法就交给数据库去处理了。下面的例子显示了如何设置和执行一个SQL语句:
TCHAR* pSQL = _T("Select * From SomeTable");
pICommandText->SetCommandText(DBGUID_DEFAULT,pSQL);
IRowset* pIRowset = NULL;
pICommandText->Execute(NULL,IID_IRowset,NULL,NULL,(IUnknown**)&pIRowset);
上面代码中,通过Execute方法我们就得到了一个结果集对象,其初始接口就是IRowset,关于结果集对象的内容我将放在下一篇文章中,这里我们主要还是聚焦在Command对象上。
之前我说过,Command对象是可以执行一段SQL语句的,甚至是多个Select语句,也就是说我们可以批量的执行的SQL语句。从本质上说执行一条SQL语句和执行一批SQL语句并没用什么区别,同时很多数据源的OLEDB提供者都必须提供批量执行SQL语句的功能,这是因为很多复杂的系统中,大多数逻辑的处理都是通过一次执行多条SQL语句才能实现的,因此掌握批量执行SQL语句的方法是至关重要的。下面的例子代码就演示了如何执行多条SQL语句:
TCHAR* pSQL = _T("Select * From SomeTable1\nSelect * From SomeTable2");
pICommandText->SetCommandText(DBGUID_DEFAULT,pSQL);
IMultipleResults* pIMultipleResults = NULL;
pICommandText->Execute(NULL,IID_IMultipleResults,NULL,NULL,
(IUnknown**)& pIMultipleResults);
上面的例子代码中引入了一个新的对象及其接口就是多结果集(MultipleResults),这个对象比较特殊,它只有这一个接口,通过它可以得到多个结果集对象及其接口(IRowset)。一般情况下,作为自己对OLEDB的封装或是应用,都应该使用IMultipleResults接口,而不是直接使用IRowset,这样做的好处就是你既可以一次只执行一条SQL语句,也可以一次执行多条SQL语句,而不用顾虑其他的问题。下面的代码框架演示了如何利用IMultipleResults接口的这一特性:
IMultipleResults* pIMultipleResults = NULL;
pICommandText->Execute(NULL,IID_IMultipleResults,NULL,NULL,
(IUnknown**)& pIMultipleResults);
IRowset* pIRowset = NULL;
DBROWCOUNT cRowsAffected = 0;//注意不要被这个参数迷惑大多数情况下它是没用的
//并不能通过它知道结果集中实际包含多少行
//循环处理每一个结果集,当然这需要你起码知道你执行SQL语句的顺序
while( S_OK == pIMultipleResults->GetResult(NULL,
DBRESULTFLAG_DEFAULT,
IID_IRowset,
cRowsAffected,
(IUnknown**)&pIRowset) )
{
......//处理每一个IRowset
pIRowset->Release();
pIRowset = NULL;
}
上面的代码就是最一般的一个遍历所有结果集的框架性代码,最好能记住或把它封装的你的代码框架中。
同前面的数据库联接对象和事务对象一样,命令(Command)对象也有自己的属性集,而且这个属性集与前面两个对象的属性集所表示的意义是不同的,前两个对象的属性只影响对象自己,而Command对象的属性则会影响因执行SQL语句而得到的结果集对象的属性,比如我们最关心的结果集是否是可以修改的,结果集是否是可以插入的,以及结果集是否是可以更新的等等。在这里还需要特别注意的一个问题就是,一个事务对象可以创建n个命令对象(n>=1)。下面的例子代码演示了如何设置命令对象(其实是结果集对象)的属性:
DBPROPSET ps[1];
DBPROP prop[2];
prop[0].dwPropertyID = DBPROP_UPDATABILITY;
prop[0].vValue.vt = VT_I4;
prop[0].vValue.lVal=DBPROPVAL_UP_CHANGE //打开Update属性
|DBPROPVAL_UP_DELETE //打开Delete属性
|DBPROPVAL_UP_INSERT; //打开Insert属性
prop[0].dwOptions = DBPROPOPTIONS_REQUIRED;
prop[0].colid = DB_NULLID;
ps[0].guidPropertySet = DBPROPSET_ROWSET; //注意属性集合的名称
ps[0].cProperties = 1;
ps[0].rgProperties = prop;
ICommandProperties * pICommandProperties = NULL;
pICommandText->QueryInterface(IID_ICommandProperties,
(void**)& pICommandProperties);
pICommandProperties->SetProperties(1,ps);//注意必须在Execute前设定属性
IRowset* pIRowset = NULL;
pICommandText->Execute(NULL,IID_IRowset,NULL,NULL,(IUnknown**)&pIRowset)
上面的例子演示了如何打开一个结果集带有更新、插入、删除的属性,这个属性打开后,我们可以直接利用结果集对象的相关方法来修改数据并提交,同时可以绕过使用等价的SQL语句(UPDATE、DELETE、INSERT等),而是使用纯粹代码的方式修改数据并提交到数据库,这是非常棒的一个特性,同时这种方式要比直接使用SQL语句的方式高效,具体如何利用这种方式的结果集,我将在后续的提高内容中详细阐述。
作为命令对象的基础性介绍就到这里,下次我将详细介绍结果集对象,以及如何真正拿到我们关心的数据。
免责声明:本站相关技术文章信息部分来自网络,目的主要是传播更多信息,如果您认为本站的某些信息侵犯了您的版权,请与我们联系,我们会即时妥善的处理,谢谢合作!