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

[本帖已阅读3935次 分值100 回复0次] 张贴资源 发回信箱 控制面板

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

提供者:ycr40 张贴时间:2004-05-01 10:24:27.0 出处:vcer.net 作者:不祥

snoopy每日一译-完美的ADO[2](下)(2004-05-01 10:24:27.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

 

可以建立两个同名方法,一个返回符号分割字符串(方法名有前缀”s”),另一个返回记录集:

STDMETHODIMP CADO::GetCustomerNameAndBalance(BSTR CustKey, _Recordset **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";

 ADOExecute(_bstr_t(SQL.str().c_str()),_bstr_t(","),Result);

 return S_OK;

}

唯一不同的是我们使用ADOExecute方法,而不使用ADOExecuteReturnDelimited方法。客户端使用:

(VB)dim MySet as object

MySet = MyADOObject.GetCustomerNameAndBalance(CustID)

(VC)

_RecorsetPtr MySet;

MyADOObject->GetCustomerNameAndBalance(CustID,&MySet);

好,现在我们可以从客户端运行SQL语句,但如何运行存储程序呢?为了实现它,我们建立2个方法。一个运行存储程序并返回分割字符串,另外一个返回记录集。让我们首先看看返回记录集的方法:

STDMETHODIMP CADO::ADOExecuteSP(BSTR SPName, VARIANT ParameterString, _Recordset **Result)

{

 VARIANT RecordsEffected;

 RecordsEffected.vt = VT_INT;

 if(m_Recordset == NULL)

  m_Recordset.CreateInstance(__uuidof(Recordset));

 if(m_Recordset->State == adStateOpen)

  m_Recordset->Close();

 try

 {

  // Now set up the command object

  m_Command->ActiveConnection = m_Connection;

  // enter the SP name

  m_Command->CommandText = _bstr_t(SPName);

  // Now execute it

  m_Recordset = m_Command->Execute(&RecordsEffected,&ParameterString,adCmdStoredProc);

  *Result = m_Recordset.Detach();

 }

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

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

 return S_OK;

}

首先,我们检查确认全局的recordset有效,但不打开:

if(m_Recordset == NULL)

 m_Recordset.CreateInstance(__uuidof(Recordset));

if(m_Recordset->State == adStateOpen)

 m_Recordset->Close();

接着,我们赋值给m_Command->ActiveConnection,并将存储程序名赋予m_Command->CommandText:

// Now set up the command object

m_Command->ActiveConnection = m_Connection;

// enter the SP name

m_Command->CommandText = _bstr_t(SPName);

最后,我们必须调用Command.Execute方法,并获得记录集:

m_Recordset = m_Command->Execute(&RecordsEffected,&ParameterString,adCmdStoredProc);

*Result = m_Recordset.Detach();

让我们看看参数列表。我们需要传送的参数是VARIANT数组(是否想到了SAFEARRAY?),在VB中,我们很容易实现:

(VB)

dim MySet as Object

MySet = MyADOObject.ADOExecuteSP("some_stored_procedure",Array("param_1",2,"param_3))

(VC)

SAFEARRAYBOUND MyBound[1];

MyBound[0].cElements = 2;

MyBound[0].lLbound = 0;

VARIANT ParamHolder;

VariantInit(ParamHolder);

ParamHolder->vt = VT_VARIANT | VT_ARRAY;

pVariant->parray = SafeArrayCreate(VT_VARIANT,2,MyBound);

VARIANT Param1(SysAllocString("123ABC");

VARIANT Param2(100);

int MyPosition;

MyPosition = 0;

SafeArrayPutElement(pVariant->parray,MyPosition,&Param1);

SafeArrayPutElement(pVariant->parray,lDimension,&Param2);

_RecordsetPtr MySet;

MyADOObject->ADOExecuteSP(_bstr_t("some_stored_procedure"),ParamHolder,&MySet);

VC中,我们需要做多一点。

当然,我们还需要一个返回符号分割字符串的姐妹方法:

STDMETHODIMP CADO::ADOExecuteSPReturnDelimited(BSTR SPName, VARIANT ParameterArray, 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;

  }

  // Now set up the command object

  m_Command->ActiveConnection = m_Connection;

  // enter the SP name

  m_Command->CommandText = _bstr_t(SPName);

  // Now execute it

  PopSet = m_Command->Execute(&RecordsEffected,&ParameterArray,adCmdStoredProc);

  if(!PopSet->adoEOF)

  {

   BSTR bstrResult = NULL;

   _bstr_t btColDelim(Delimiter);

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

   _bstr_t btNullExp(L"");

   PopSet->raw_GetString(adClipString,-1,btColDelim,btRowDelim,btNullExp,&bstrResult);

   *Result = _variant_t(bstrResult);

  }

  else

   *Result = _variant_t("");

 }

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

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

 return S_OK;

}

客户端调用:

(VB)

MyResult = MyADOObject.ADOExecuteSPReturnDelimited("some_stored_procedure",Array("param_1",2,"param_3), ",")

(VC)

SAFEARRAYBOUND MyBound[1];

MyBound[0].cElements = 2);

MyBound[0].lLbound = 0;

VARIANT ParamHolder;

VariantInit(ParamHolder);

ParamHolder->vt = VT_VARIANT | VT_ARRAY;

pVariant->parray = SafeArrayCreate(VT_VARIANT,2,MyBound);

VARIANT Param1(SysAllocString("123ABC");

VARIANT Param2(100);

int MyPosition;

MyPosition = 0;

SafeArrayPutElement(pVariant->parray,MyPosition,&Param1);

SafeArrayPutElement(pVariant->parray,lDimension,&Param2);

VARIANT MyResult;;

MyADOObject->ADOExecuteSPReturnDelimited(_bstr_t("some_stored_procedure"),ParamHolder,_bstr_t(","),&MyResult);

现在,让我们来做最后一步:加入一个基于存储程序的返回记录集和符号分割字符串的专用方法。客户端只需要知道这个方法需要一个Rep key作为参数。在我们的数据库中,我们建立了一个存储程序GetEmployeeInfo,它需要一个参数,就是employee ID。

STDMETHODIMP CADO: GetEmployeeInfo (BSTR RepKey, _Recordset **Result)

{

 SAFEARRAYBOUND MyBound[0];

 MyBound[0].cElements = 1);

 VARIANT ParamHolder;

 VariantInit(ParamHolder);

 ParamHolder->vt = VT_VARIANT | VT_ARRAY;

 pVariant->parray = SafeArrayCreate(VT_VARIANT,1,MyBound);

 VARIANT Param1(RepKey);

 int MyPosition;

 MyPosition = 0;

 SafeArrayPutElement(pVariant->parray,MyPosition,&Param1);

 ADOExecuteSP(_bstr_t("GetEmployeeInfo"),ParamHolder,Result);

}

客户端调用:

(VB)

dim MySet as Object

MySet = MyADOObject.GetEmployeeInfo("123ABC")

(VC)

_RecordsetPtr MySet;

MyADOObject->GetEmployeeInfo(_bstr_t("123ABC"),&MySet);

客户端的调用很简单。客户端无须知道存储程序的名字,存储程序的参数,怎样使用Command对象。对于VC客户端来说,它无须知道如何使用SAFEARRAY。

现在看看返回符号分割字符串的方法:

STDMETHODIMP CADO:: sGetEmployeeInfo (BSTR RepKey, VARIANT *Result)

{

 SAFEARRAYBOUND MyBound[0];

 MyBound[0].cElements = 1);

 VARIANT ParamHolder;

 VariantInit(ParamHolder);

 ParamHolder->vt = VT_VARIANT | VT_ARRAY;

 pVariant->parray = SafeArrayCreate(VT_VARIANT,1,MyBound);

 VARIANT Param1(RepKey);

 int MyPosition;

 MyPosition = 0;

 SafeArrayPutElement(pVariant->parray,MyPosition,&Param1);

 ADOExecuteSPReturnDelimited(_bstr_t("GetEmployeeInfo"),ParamHolder,_bstr_t(","),Result);

}

客户端使用:

(VB)

MyResult = MyADOObject.sGetEmployeeInfo("123ABC")

(VC)

VARIANT MyResult;

MyADOObject->sGetEmployeeInfo(_bstr_t("123ABC"),&MyResult);

好,现在我们知道了:

1、 建立一个ATL COM对象很容易。只要跟着向导做就可以了。

2、 可以使用向导往接口中添加方法和属性。

3、 在ATL对象中添加ADO支持和在应用程序中添加一样简单。

4、 如何去传送VARIANT, BSTR, 和 SAFEARRAY类型的参数。

5、 VB和VC客户端调用方法的不同。

6、 怎样去使用SAFEARRAY, _bstr_t 和_variant_t。

7、 怎样包封基于查询的ADO方法去返回一个记录集或一个符号分割字符串。

8、 通过Command对象的接口,执行存储程序,并返回一个记录集或一个符号分割字符串。

9、 怎样隐藏数据库的信息,使客户端无须知道数据库怎样组织。

现在,我向你介绍一些很酷的技巧!

当我们开发ASP网页的时候我们会经常使用ADO COM对象。在网页中,我们也经常使用表格。现在我告诉你如何去基于查询或存储程序得到一个表格:

STDMETHODIMP CADO::ADOMakeWebTable(BSTR SQL, VARIANT *Result)

{

 VARIANT RecordsEffected;

 RecordsEffected.vt = VT_INT;

 BSTR bstrResult = NULL;

 std::strstream Holder;

 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;

  }

  PopSet = m_Connection->Execute((char*)_bstr_t(SQL),&RecordsEffected,adCmdText);

  if(!PopSet->adoEOF)

  {

   _bstr_t btColDelim(L"</TD><TD>");

   _bstr_t btRowDelim(L"</TD</TR><TR><TD>");

   _bstr_t btNullExp(L"");

   PopSet->raw_GetString(adClipString,-1,btColDelim,btRowDelim,btNullExp,&bstrResult);

   Holder.str("");

   Holder<<"<TABLE BORDER="<<m_Border<<" BGCOLOR="<<(char*)m_BGColor<<" STYLE='color="<<(char*)m_FGColor<<"'><TR><TD>"<<(char*)_bstr_t(bstrResult)<<"</TD></TR></TABLE>";

   *Result = _variant_t(_bstr_t(Holder.str().c_str()));

  }

  else

   *Result = _variant_t("");

 }

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

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

 return S_OK;

}

在ASP网页中:

(ASP)

Response.write MyADOObject.ADOMakeWebTable("SELECT * FROM some_table")

这样在你的网页中就有了一个表,我们经常使用它。实际上,我们还有一个方法改变表格中的参数:

STDMETHODIMP CADO::ADOSetWebTableInfo(int Border, BSTR FGColor, BSTR BGColor)

{

 // This is where we set up the variables for the WEB tables

 if(Border <0 || Border > 5)

  m_Border = 0;

 else

  m_Border = Border;

 m_FGColor = FGColor;

 m_BGColor = BGColor;

 return S_OK;

}

这个方法允许我们改变表格的边框大小,前景和背景颜色。你很容易加入更多的选择。

下面这个方法是存储程序版:

STDMETHODIMP CADO::ADOMakeSPWebTable(BSTR SPName, VARIANT ParamArray, VARIANT *Result)

{

 VARIANT RecordsEffected;

 RecordsEffected.vt = VT_INT;

 BSTR bstrResult = NULL;

 std::strstream Holder;

 *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;

  }

  // Now set up the command object

  m_Command->ActiveConnection = m_Connection;

  // enter the SP name

  m_Command->CommandText = _bstr_t(SPName);

  // Now execute it

  PopSet = m_Command->Execute(&RecordsEffected,&ParamArray,adCmdStoredProc);

  if(!PopSet->adoEOF)

  {

   _bstr_t btColDelim(L"</TD><TD>");

   _bstr_t btRowDelim(L"</TD</TR><TR><TD>");

   _bstr_t btNullExp(L"");

   PopSet->raw_GetString(adClipString,-1,btColDelim,btRowDelim,btNullExp,&bstrResult);

   Holder.str("");

   Holder<<"<TABLE BORDER="<<m_Border<<" BGCOLOR="<<(char*)m_BGColor<<" STYLE='color="<<(char*)m_FGColor<<"'><TR><TD>"<<(char*)_bstr_t(bstrResult)<<"</TD></TR></TABLE>";

   *Result = _variant_t(_bstr_t(Holder.str().c_str()));

  }

  else

   *Result = _variant_t("");

 }

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

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

 return S_OK;

}

当然,你可以包封成专用方法,像:

STDMETHODIMP CADO::GetEmployeeTable(VARIANT *Result)

{

 ADOMakeSPWebTable(_bstr_t("EmployeeList"),m_vNull,Result);

 return S_OK;

}

在ASP网页中:

(ASP)

Response.Write MyADOObject.GetEmployeeTable()

结论:

如果你已经读到了这里,我希望本文能对你有所帮助。我希望读过本文后,你使用ADO的时候会容易一些。我希望有更多关于ADO的书,也许我会献丑写一本。但在此之前,我希望你喜欢ADO和ATL。一旦你理解了它们是怎样工作的,你会发现实际上它们并不差。祝你好运!

(完)

注:转载文章需注明来源:VCer.net 文章地址:http://vcer.net/2027.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% (2 票)
B: 评分 5 0% (0 票)
C: 评分 0 0% (0 票)
D: 评分 -5 0% (0 票)
E: 评分 -10 0% (0 票)