打印本文 打印本文 关闭窗口 关闭窗口
ADO数据库编程入门
作者:武汉SEO闵涛  文章来源:敏韬网  点击数2859  更新时间:2009/4/23 10:48:18  文章录入:mintao  责任编辑:mintao
法主要是使用SafeArray挺麻烦。
方法三:就是用定义绑定类的方法(详见后面的介绍)。
7、删除记录
调用Recordset的Delete方法就行了,删除的是当前记录。要了解Delete的其它用法请查阅参考文献。
 try{
  m_pRecordset->MoveFirst();  
  while(m_pRecordset->adoEOF==VARIANT_FALSE) 
  {
   CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
    (_variant_t("姓名"))->Value);
   if(::MessageBox(NULL,"姓名="+sName+"\n删除她吗?",
    "提示",MB_YESNO | MB_ICONWARNING)==IDYES)
   {
    m_pRecordset->Delete(adAffectCurrent);   
    m_pRecordset->Update();
   }
   m_pRecordset->MoveNext();   
  }
 }//try
 catch (_com_error &e)
 {
  ::MessageBox(NULL,"又出毛病了。","提示",MB_OK | MB_ICONWARNING);
 }
8、使用带参数的命令
Command对象所代表的就是一个Provider能够理解的命令,如SQL语句等。使用Command对象的关键就是把表示命令的语句设置到CommandText属性中,然后调用Command对象的Execute方法就行了。一般情况下在命令中无需使用参数,但有时使用参数,可以增加其灵活性和效率。
(1). 建立连接、命令对象和记录集对象
本例中表示命令的语句就是一个SQL语句(SELECT语句)。SELECT语句中的问号?就代表参数,如果要多个参数,就多放几个问号,每个问号代表一个参数。
_ConnectionPtr  Conn1;
_CommandPtr     Cmd1;
ParametersPtr   *Params1 = NULL;   // Not an instance of a smart pointer.
_ParameterPtr   Param1;
_RecordsetPtr   Rs1;

try
{
 // Create Connection Object (1.5 Version)
 Conn1.CreateInstance( __uuidof( Connection ) );
 Conn1->ConnectionString = bstrConnect;
    Conn1->Open( bstrEmpty, bstrEmpty, bstrEmpty, -1 );
    // Create Command Object
    Cmd1.CreateInstance( __uuidof( Command ) );
    Cmd1->ActiveConnection = Conn1;
    Cmd1->CommandText  = _bstr_t("SELECT * FROM mytable WHERE age< ?");
}//try
要注意命令对象必须与连接对象关联起来才能起作用,本例中将命令对象的ActiveConnection属性设置为连接对象的指针,即为此目的:
Cmd1->ActiveConnection = Conn1;
 (2). 创建参数对象,并给参数赋值
// Create Parameter Object
Param1 = Cmd1->CreateParameter( _bstr_t(bstrEmpty),
       adInteger,
       adParamInput,
       -1,
        _variant_t( (long) 5) );
Param1->Value = _variant_t( (long) 5 );
Cmd1->Parameters->Append( Param1 );
用命令对象的方法来创建一个参数对象,其中的长度参数(第三个)如果是固定长度的类型,就填-1,如果是字符串等可变长度的就填其实际长度。Parameters是命令对象的一个容器,它的Append方法就是把创建的参数对象追加到该容器里。Append进去的参数按先后顺序与SQL语句中的问号从左至右一一对应。
(3). 执行命令打开记录集
// Open Recordset Object
Rs1 = Cmd1->Execute( &vtEmpty, &vtEmpty2, adCmdText );
但要注意,用Command和Connection对象的Execute方法得到的Recordset是只读的。因为在打开Recordset之前,我们无法设置它的LockType属性(其默认值为只读)。而在打开之后设置LockType不起作用。
我发现用上述方法得到记录集Rs1后,不但Rs1中的记录无法修改,即使直接用SQL语句修改同一表中任何记录都不行。
要想能修改数据,还是要用Recordset自己的Open方法才行,如:
 try{
   m_pRecordset->Open((IDispatch *) Cmd1, vtMissing,
    adOpenStatic, adLockOptimistic, adCmdUnspecified);
  }
  catch (_com_error &e)
  {
   ::MessageBox(NULL,"mytable表不存在。","提示",MB_OK | MB_ICONWARNING);
  }
Recordset对象的Open方法真是太好了,其第一个参数可以是SQL语句、表名字、命令对象指针等等。
9、响应ADO的通知事件
通知事件就是当某个特定事件发生时,由Provider通知客户程序,换句话说,就是由Provider调用客户程序中的一个特定的方法(即事件的处理函数)。所以为了响应一个事件,最关键的就是要实现事件的处理函数。
(1). 从ConnectionEventsVt接口派生出一个类
为了响应_Connection的通知事件,应该从ConnectionEventsVt接口派生出一个类:
class CConnEvent : public ConnectionEventsVt
{
private:
      ULONG   m_cRef;
public:
      CConnEvent() { m_cRef = 0; };
      ~CConnEvent() {};

      STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);
      STDMETHODIMP raw_InfoMessage(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);
      STDMETHODIMP raw_BeginTransComplete(
         LONG TransactionLevel,
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection);
  ......
};
(2). 实现每一个事件的处理函数(凡是带raw_前缀的方法都把它实现了):
STDMETHODIMP CConnEvent::raw_InfoMessage(
         struct Error *pError,
         EventStatusEnum *adStatus,
         struct _Connection *pConnection)
         {
         *adStatus = adStatusUnwantedEvent;
         return S_OK;
         };

有些方法虽然你并不需要,但也必须实现它,只需简单地返回一个S_OK即可。但如果要避免经常被调用,还应在其中将adStatus参数设置为adStatusUnwantedEvent,则在本次调用后,以后就不会被调用了。
另外还必须实现QueryInterface, AddRef, 和Release三个方法: 
 STDMETHODIMP CConnEvent::QueryInterface(REFIID riid, void ** ppv)
 {
      *ppv = NULL;
      if (riid == __uuidof(IUnknown) ||
          riid == __uuidof(ConnectionEventsVt)) *ppv = this;
      if (*ppv == NULL)
         return ResultFromScode(E_NOINTERFACE);
      AddRef();
      return NOERROR;
   }
   STDMETHODIMP_(ULONG) CConnEvent::AddRef() { return ++m_cRef; };
   STDMETHODIMP_(ULONG) CConnEvent::Release()
{
if (0 != --m_cRef) return m_cRef;
delete this;
return 0;
}
(3). 开始响应通知事件
 // Start using the Connection events
 IConnectionPointContainer   *pCPC = NULL;
 IConnectionPoint         *pCP = NULL;

 hr = pConn.CreateInstance(__uuidof(Connection));
   if (FAILED(hr)) return;

 hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
      (void **)&pCPC);
 if (FAILED(hr)) return;
 hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
 pCPC->Release();
 if (FAILED(hr)) return;

 pConnEvent = new CConnEvent();
   hr = pConnEvent->QueryInterface(__uuidof(IUnknown), (void **) &pUnk);
   if (FAILED(hr)) return rc;
   hr = pCP->Advise(pUnk, &dwConnEvt);
   pCP->Release();
   if (FAILED(hr)) return;

 pConn->Open("dsn=Pubs;", "sa", "", adConnectUnspecified);
也就是说在连接(Open)之前就做这些事。
(4). 停止响应通知事件
 pConn->Close();
 // Stop using the Connection events
  hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
      (void **) &pCPC);
   if (FAILED(hr)) return;
   hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
   pCPC->Release();
   if (FAILED(hr)) return rc;
   hr = pCP->Unadvise( dwConnEvt );
   pCP->Release();
   if (FAILED(hr)) return;
在连接关闭之后做这件事。
10、邦定数据
定义一个绑定类,将其成员变量绑定到一个指定的记录集,以方便于访问记录集的字段值。
(1). 从CADORecordBinding派生出一个类:
class CCustomRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs)
   ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname,
         sizeof(m_szau_fname), lau_fnameStatus, false)
   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname,
         sizeof(m_szau_lname), lau_lnameStatus, false)
   ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szphone,   
         sizeof(m_szphone),    lphoneStatus,    true)
END_ADO_BINDING()

public:
   CHAR   m_szau_fname[22];
   ULONG   lau_fnameStatus;
   CHAR   m_szau_lname[42];
   ULONG   lau_lnameStatus;
   CHAR   m_szphone[14];
   ULONG   lphoneStatus;
};
其中将要绑定的字段与变量名用BEGIN_ADO_BINDING宏关联起来。每个字段对应于两个变量,一个存放字段的值,另一个存放字段的状态。字段用从1开始的序号表示,如1,2,3等等。
特别要注意的是:如果要绑定的字段是字符串类型,则对应的字符数组的元素个数一定要比字段长度大2(比如m_szau_fname[22],其绑定的字段au_fname的长度实际是20),不这样绑定就会失败。我分析多出的2可能是为了存放字符串结尾的空字符null和BSTR字符串开头的一个字(表示BSTR的长度)。这个问题对于初学者来说可能是一个意想不到的问题。
CADORecordBinding类的定义在icrsint.h文件里,内容是:
class CADORecordBinding
{
public:
 STDMETHOD_(const ADO_BINDING_ENTRY*, GetADOBindingEntries) (VOID) PURE;
};

BEGIN_ADO_BINDING宏的定义也在icrsint.h文件里,内容是:
#define BEGIN_ADO_BINDING(cls) public: \
typedef cls ADORowClass; \
const ADO_BINDING_ENTRY* STDMETHODCALLTYPE GetADOBindingEntries() { \
static const ADO_BINDING_ENTRY rgADOBindingEntries[] = {

ADO_VARIABLE_LENGTH_ENTRY2宏的定义也在icrsint.h文件里:
#define ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status, Modify)\
 {Ordinal, \
 DataType, \
 0, \
 0, \
&

上一页  [1] [2] [3] [4]  下一页

打印本文 打印本文 关闭窗口 关闭窗口