目前位置: VCer资源中心 >>> VCer文章 >>> 数据库

[本帖已阅读3301次 分值90 回复1次] 张贴资源 发回信箱 控制面板

snoopy每日一译-完美的ADO[2](中)

提供者:ycr40 张贴时间:2004-04-30 17:33:39.0 出处:vcer.net 作者:不祥

snoopy每日一译-完美的ADO[2](中)(2004-04-30 17:33:39.0)


snoopy


 
级别: VCer排长
头衔: VCer会员

经验: 1285
作品: 28
分会: 华南分会
注册: 2004-04-15 09:22:35.0
登录: 2004-05-18 08:45:59.0

snoopy每日一译-完美的ADO[2](中)

by:Bob Place 1999.6.27

from:codeGuru

翻译:snoopy

 

SAFEARRAY(sorry没有它的封装类)是从容易COM对象传送和接受数据的方法之一(当然,容易是相对而言)。

SAFEARRAYBOUND可以用来设置SAFEARRY的范围。你必须设定上界和下界。

让我们看看例子,假设我们要建立一个10行2列的SAFEARRAY:

//2列,因此MyBound有2维

SAFEARRAYBOUND MyBound[2];

//10行

MyBound[0].cElements = 10;

MyBound[1].cElements = 10;

//设定下界为0

MyBound[0].lLbound = 0;

MyBound[1].lLBound = 0;

 

//定义VARIANT指针

VARIANT *pVariant;

VariantInit(pVariant);

pVariant->vt = VT_VARIANT | VT_ARRAY;

pVariant->parray = SafeArrayCreate(VT_VARIANT2MyBound);//建立SAFEARRAY

 

或者你可以直接使用SAFEARRAY类型:

SAFEARRAY pArray = SafeArrayCreate(VT_VARIANT2MyBound);

 

现在,让我们给SAFEARRAY填充一些内容:

VARIANT Column1(SysAllocString("COW");

VARIANT Column2(100);

int MyPosition[2];

MyPosition[0] = 0;// 0列

MyPosition[1] = 0;// 0行

SafeArrayPutElement(pVariant->parrayMyPosition&Column1);

MyPosition[0] = 1;//1列

MyPosition[1] = 0;//0 行

SafeArrayPutElement(pVariant->parrayMyPosition&Column2);

 

现在,让我们回到COM组件中。

我们需要什么方法呢?

这个问题轮到你回答。在本文中,我只覆盖了几种方法,你可以加入你能想到的任何方法。首先让我们来考虑打开和关闭连接的方法:

打开一个连接:右击IADO选择New Method,参数如下:

[in] BSTR ConnectionString [in] BSTR UserId [in] BSTR Password [outretval] int *Result

[in]的意思是参数从客户端传输过来,[out]的意思是从服务器端送到客户端,[retval]的意思是对于VB等语言的客户端来说,返回值。

 

STDMETHODIMP CADO::ADOOpenConnection(BSTR ConnectionString BSTR UserId BSTR Password int *Result)

{

       _bstr_t TempConnectionString(ConnectionString);

       if(TempConnectionString.length() <1)

       {

              // 如果没有连接串,连接默认数据库          

// It will change with each ADO COM Object

              std::string Holder = "MY_DSN";

              // Now open the connection only if it is not already open

              try

              {

                     if(m_Connection->State != adStateOpen)

                     {

                            HRESULT hr = m_Connection->Open(_bstr_t(Holder.c_str())_bstr_t("Admin")_bstr_t("ORAIDERS")-1);

                            if(hr != S_OK)

                            {

                                   Result = 0;

                                   return S_FALSE;

                            }

                     }

              }

              catch(_com_error &e){} // error handling here

              catch(...){}// all other exceptions here

              return S_OK;

       }

       else

       {

              try

              {

                     m_Connection->Open(ConnectionStringUserIdPasswordadAsyncConnect);

              }

              catch(_com_error &e){} // error handling here

              catch(...){}// all other exceptions here

              return S_OK;

       }

}

 

注意到我们使用了std string模板,另外我们检查可能出现的错误。当发生错误的时候,你应该向客户端发送恰当的消息。在本文中,因为我很懒,所以只有最简单的捕抓错误语句。调用这个方法:

(VB)

Result = MyADOObject.ADOOpenConnection """"""

(VC)

MyADOObject->ADOOpenConnection(""""""&Result);

同样地加入关闭连接的方法:

STDMETHODIMP CADO::ADOCloseConnection()

{

       try

       {

              if(m_Connection->State == adStateOpen)

                     m_Connection->Close();

       }

       catch(_com_error &e){} // error handling here

       catch(...){}// all other exceptions here

       return S_OK;

}

现在,让我们考虑做点实际的工作。我们决定用3种方法从COM对象获取数据。第一种是返回记录集,第二种返回一个符号分割字符串,第三种返回单一的一个值。我们还决定可以使用SQL和存储程序两种方式来获取数据。因此,我们需要添加如下的方法:

ADOExecuteReturnDelimited)(/*[in]*/ BSTR SQL /*[in]*/ BSTR Delimiter /*[outretval]*/VARIANT *Result);

 

ADOExecute)(/*[in]*/ BSTR SQL /*[outretval]*/ _Recordset **Result);

 

ADOExecuteSPReturnDelimited)(/*[in]*/ BSTR SPName /*[in]*/ VARIANT ParameterArray /*[in]*/BSTR Delimiter /*[outretval]*/ VARIANT *Result);

 

ADOExecuteSP)(/*[in]*/ BSTR SPName /*[in]*/ VARIANT ParameterArray /*[outretval]*/ _Recordset **Result);

 

ADOGetVariant)(/*[in]*/ BSTR SQL /*[outretval]*/ VARIANT *Result);

 

添加这些方法,你只需右击接口,然后选择New Method…。但是,且慢…,当你试图编译它们的时候,你会得到一些发生了错误的信息,像:没有_Recordset类型等等。改正这些错误,你需要加入以下一些东西到你的程序中:

首先:Helper.h文件,包含下列语句:

struct _Recordset;

#if !defined(__cplusplus) || defined(CINTERFACE)

typedef struct _Recordset _Recordset;

#endif

其次:建立一个helper.idl文件,包含如下语句:

import "msado15.idl";

然后:在你的ADO.h中加入:

#include "helper.h"

最后:在你的项目的idl文件中,加入:

import "helper.idl";

 

让我们看看以下代码:

 

STDMETHODIMP CADO::ADOExecute(BSTR SQL _Recordset **Result)

{

       VARIANT RecordsEffected;

       RecordsEffected.vt = VT_INT;

       try

       {

              if(m_Recordset == NULL)

                     m_Recordset.CreateInstance(__uuidof(Recordset));

              if(m_Recordset->State == adStateOpen)

                     m_Recordset->Close();

              m_Recordset = m_Connection->Execute(_bstr_t(SQL)&RecordsEffectedadCmdText);

              *Result = m_Recordset.Detach();

       }

       catch(_com_error &e){} // error handling here

       catch(...){}// all other exceptions here

}

首先,我们做的第一件事情是确认m_Recordset可用并关闭:

if(m_Recordset == NULL)

       m_Recordset.CreateInstance(__uuidof(Recordset));

if(m_Recordset->State == adStateOpen)

       m_Recordset->Close();

然后使用m_Connection执行SQL语句:

m_Recordset = m_Connection->Execute(_bstr_t(SQL)&RecordsEffectedadCmdText);

最后:

*Result = m_Recordset.Detach();

 

客户端使用:

(VB)

Dim MySet as Object

Object = MyADOObject.ADOExecute("SELECT * FROM some_table")

 

(VC)

_RecordsetPtr MySet;

MyADOObject->ADOExecute(_bstr_t("SELECT * FROM some_table)&MySet);

 

这些方法很简单,我们所做的只是将连接、执行和返回包封起来。你或许会问:实际上简单地在客户端建立连接并执行SQL语句不就完了吗?干嘛费那么大的劲?

我们要记住:我们要建立的一条道路是让客户端不知道我们的数据如何组织,在COM中怎样访问数据。以上建立的方法会用于COM对象里面,以方便编写一些特定的方法。你或许可以选择将它们隐藏,使客户端开发人员无法访问,只要你喜欢。

 

好,让我们继续:

STDMETHODIMP CADO::ADOExecuteReturnDelimited(BSTR SQL BSTR Delimiter VARIANT *Result)

{

       VARIANT RecordsEffected;

       RecordsEffected.vt = VT_INT;

       *Result = _variant_t("");

       try

       {

              _RecordsetPtr PopSet;

              PopSet.CreateInstance(__uuidof(Recordset));

              PopSet->CursorLocation = adUseClient;

              if(m_Connection->State != adStateOpen)

              {

                     *Result = _variant_t("VISP_COM_ERROR_CONNECTION_NOT_OPEN");

                     return S_FALSE;

              }

              ADOExecute((char*)_bstr_t(SQL)&PopSet);

              if(!PopSet->adoEOF)

              {

                     BSTR bstrResult = NULL;

                     _bstr_t btColDelim(L"");

                     _bstr_t btRowDelim(L"\r\n");

                     _bstr_t btNullExp(L"");

                     PopSet->raw_GetString(adClipString-1btColDelimbtRowDelimbtNullExp&bstrResult);

                     *Result = _variant_t(bstrResult);

              }

              else

                     *Result = _variant_t("");

       }

       catch(_com_error &e){} // error handling here

       catch(...){}// all other exceptions here

       return S_OK;

}

你可看到,我们在这使用了一个本地的_RecordsetPtr实例:

_RecordsetPtr PopSet;

PopSet.CreateInstance(__uuidof(Recordset));

PopSet->CursorLocation = adUseClient;

 

需要注意的是:GetString方法会抛出一个未处理异常,所以不要用GetString方法,你可以使用raw_GetString方法。我们使用了ADOExecute而不使用Connection. Execute方法,是因为可以少打几个字:

ADOExecute((char*)_bstr_t(SQL)&PopSet);

然后我们取回记录集,检查有没有记录,接着使用raw_GetString方法转换记录集为符号分割字符串:

if(!PopSet->adoEOF)

{

       BSTR bstrResult = NULL;

       _bstr_t btColDelim(L"");

       _bstr_t btRowDelim(L"\r\n");

       _bstr_t btNullExp(L"");

       PopSet->raw_GetString(adClipString-1btColDelimbtRowDelimbtNullExp&bstrResult);

       *Result = _variant_t(bstrResult);

}

else

       *Result = _variant_t("");

 

现在你可以实验用比较的记录集转化到字符串中,我没有试过可用多打的记录集,如果谁试过,我很感兴趣这个大小是多少。客户端使用:

(VB)

ResultString = MyADOObject.ADOExecuteReturnDelimited("SELECT * FROM some_table""")

 

(VC)

BSTR ResultString;

MyADOObject->ADOExecuteReturnDelimited(_bstr_t("SELECT * FROM some_table")_bstr_t("")&Result);

 

好,现在我们已经建立了一个ATL对象,加入了ADO支持,加入客4个方法。所有这些都只是简单地包封了ADO方法。让我们考虑,加入你的几个应用程序需要一个customer name 和一个current balance。在数据库中,它们保存在两个表中:cust_info和cust_credit。其中重要的域是cust_info.cust_id cust_info.cust_name 和cust_credit.cust_balance。对于别的应用程序来说,它们需要知道表名,域名和cust_id。

如果我们将和数据库比较密切的信息放到隐藏到方法中:

STDMETHODIMP CADO::sGetCustomerNameAndBalance(BSTR CustKey VARIANT *Result)

{

       // std stream used to create the SQL statement

       std::strstream SQL;

       // Put the BSTR into something we can deal with

       _bstr_t Holder(CustKey);

       SQL.str("");

       SQL<<"SELECT a.cust_name b.cust_balance FROM cust_info as a cust_credit as b WHERE a.cust_id = '<<(char*)Holder<<" AND a.cust_key = b.cust_key";

       ADOExecuteReturnDelimited(_bstr_t(SQL.str().c_str())_bstr_t("")Result);

       return S_OK;

}

Whoa 简单吗?让我们看看我们做了些什么?首先,我们建立了一个strstream ,这是一个STD模板:

 

std::strstream SQL;

接着,我们将客户端从来的BSTR转换成__bstr_t。我总是喜欢用_bstr_t,因为它容易使用。当然还有其他方法可以使用(CcomBstr是其中之一),我喜欢_bstr_t,永远…。我们使用SQL语句获取信息,客户端无须知道:

SQL.str("");// this resets it to nothing

SQL<<"SELECT a.cust_name b.cust_balance FROM cust_info as a cust_credit as b WHERE a.cust_id = '<<(char*)_bstr_t(CustKey)<<" AND a.cust_key = b.cust_key";

最后,我们调用我们几分钟前建立的ADOExecuteReturnDelimited()方法:

ADOExecuteReturnDelimited(_bstr_t(SQL.str().c_str())_bstr_t("")Result);

现在我们有了一个方法,它向客户端返回重要的信息,但是客户端无须知道我们的数据库是怎样组织的。当然客户端要知道COM对象有这样一个方法,并且这个方法需要一个customer id。让我们看看VB和VC客户端如何使用:

(VB)

MyResult = MyADOObject.sGetCustomerNameAndBalance(SomeCustID)

 

(VC)

VARIANT MyResult;

MyADOObject.->sGetCustomerNameAndBalance(SomeCustIDMyResult);

 

现在,例子还相当简单。但是我想你可以看到了一个ADO COM对象是多么强大了吧。客户端不知道(也没有必要知道)怎么去连接数据库,什么表名,怎样使用记录集,怎样从记录集中抽取需要的信息。客户端只需要调用COM对象的方法,这个方法需要一个customer ID,并且会返回一个逗号分割的包含customer name 和 balance的字符串就够了。

(未完待续)

注:转载文章需注明来源:VCer.net 文章地址:http://vcer.net/2014.html

  如果你觉得VCer.net不错,而且你愿意为VCer.net捐赠一元钱,那么点击后面的捐赠按钮吧:) vcer.net捐赠

[回复该贴] [加入个人书签]
[连载系列]

[1] snoopy每日一译-完美的ADO
[2] snoopy每日一译-完美的ADO[2](上)
[3] snoopy每日一译-完美的ADO[2](中)
[4] snoopy每日一译-完美的ADO[2](下)

[投票结果]

A: 评分 10 100% (1 票)
B: 评分 5 0% (0 票)
C: 评分 0 0% (0 票)
D: 评分 -5 0% (0 票)
E: 评分 -10 0% (0 票)

 


re:snoopy每日一译-完美的ADO[2](中)
嗯,看来还有(下)~

bluejoe 于 2004-04-30 18:41:35.0 编辑 [回复该贴]