VC++ 实战OLEDB编程(三)

来源:互联网 发布:python while循环假死 编辑:程序博客网 时间:2024/05/20 17:26

 

接下来我们详细的讨论有关命令(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语句的方式高效,具体如何利用这种方式的结果集,我将在后续的提高内容中详细阐述。

    作为命令对象的基础性介绍就到这里,下次我将详细介绍结果集对象,以及如何真正拿到我们关心的数据。

    (未完待续)

原创粉丝点击