`
hereson
  • 浏览: 1450997 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

ADO编程实用宝典

阅读更多

 

 

 
一 引入ADO类型库
Activex Objects Model首先是一组COM对象模型。所以通常我们需要引入其类型库,以下是引入类型库的语句
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace raw_interfaces_only rename ("EOF","ADOEOF")
     这里no_namespace 表示引入的类型不会放在名字空间里。raw_interfaces_only表示只使用COM的原始接口方法调用,而不使用包装接口。区别就是包装接口总是用异常的方式报错,而原始接口方法通过返回HRESULT值报告信息;同时,参数类型和数目略有区别。
    编译器将生成msado15.tlh文件,该文件包含了ADO中的各种类型
创建与数据库的连接
       首先我们声明连接对象。msado15.tlh文件中对于连接对象如下说明,
struct __declspec(uuid("00000514-0000-0010-8000-00aa006d2ea4"))
Connection;
    // [ default ] interface _Connection
    // [ default, source ] dispinterface ConnectionEvents
以下是创建连接对象的代码:
CComPtr<_Connection> pCon;
HRESULT hr=pCon.CoCreateInstance(__uuidof(Connection));
或者 HRESULT hr=pCon.CoCreateInstance(L"ADODB.Connection");
     很多书上介绍使用_ConnectionPtr,实际上都是智能指针,只是个人习惯而已,这些智能指针类通过重载operator ->()函数暴露了接口方法。
    然后使用udl文件和某数据库连接,我非常喜欢使用udl文件的方式,而不喜欢使用字符串与数据库连接,因为如果不小心差了一个空格,数据库连接就会出错,有时候错误一开始不会发生,但是说不定哪天就冒出来了。所以我的文章最后会给出数据库的连接字符串,但是不推荐使用。
    pCon->put_ConnectionString(CComBSTR(L"File Name=c:\\fqw.udl"));
     hr=pCon->Open(L"",L"",L"",NULL);
 
使用_Connection接口
 
Connection;
    // [ default ] interface _Connection
// [ default, source ] dispinterface ConnectionEvents
通过msado15.tlh文件中的片断我们可以看出Connection对象实现了_Connection接口
查看_Connection接口的定义:
struct __declspec(uuid("00000534-0000-0010-8000-00aa006d2ea4"))
_ADO : IDispatch
{
    //
    // Raw methods provided by interface
    //
 
      virtual HRESULT __stdcall get_Properties (
        /*[out,retval]*/ struct Properties * * ppvObject ) = 0;
};
 
struct __declspec(uuid("00000515-0000-0010-8000-00aa006d2ea4"))
Connection15 : _ADO
{
    //
    // Raw methods provided by interface
    //
 
      virtual HRESULT __stdcall get_ConnectionString (
        /*[out,retval]*/ BSTR * pbstr ) = 0;
      virtual HRESULT __stdcall put_ConnectionString (
        /*[in]*/ BSTR pbstr ) = 0;
      virtual HRESULT __stdcall get_CommandTimeout (
        /*[out,retval]*/ long * plTimeout ) = 0;
      virtual HRESULT __stdcall put_CommandTimeout (
        /*[in]*/ long plTimeout ) = 0;
      virtual HRESULT __stdcall get_ConnectionTimeout (
        /*[out,retval]*/ long * plTimeout ) = 0;
     virtual HRESULT __stdcall put_ConnectionTimeout (
        /*[in]*/ long plTimeout ) = 0;
      virtual HRESULT __stdcall get_Version (
        /*[out,retval]*/ BSTR * pbstr ) = 0;
      virtual HRESULT __stdcall Close ( ) = 0;
      virtual HRESULT __stdcall Execute (
        /*[in]*/ BSTR CommandText,
        /*[out]*/ VARIANT * RecordsAffected,
        /*[in]*/ long Options,
        /*[out,retval]*/ struct _Recordset * * ppiRset ) = 0;
      virtual HRESULT __stdcall BeginTrans (
        /*[out,retval]*/ long * TransactionLevel ) = 0;
      virtual HRESULT __stdcall CommitTrans ( ) = 0;
      virtual HRESULT __stdcall RollbackTrans ( ) = 0;
      virtual HRESULT __stdcall Open (
        /*[in]*/ BSTR ConnectionString,
        /*[in]*/ BSTR UserID,
        /*[in]*/ BSTR Password,
        /*[in]*/ long Options ) = 0;
      virtual HRESULT __stdcall get_Errors (
        /*[out,retval]*/ struct Errors * * ppvObject ) = 0;
      virtual HRESULT __stdcall get_DefaultDatabase (
        /*[out,retval]*/ BSTR * pbstr ) = 0;
      virtual HRESULT __stdcall put_DefaultDatabase (
        /*[in]*/ BSTR pbstr ) = 0;
      virtual HRESULT __stdcall get_IsolationLevel (
        /*[out,retval]*/ enum IsolationLevelEnum * Level ) = 0;
      virtual HRESULT __stdcall put_IsolationLevel (
        /*[in]*/ enum IsolationLevelEnum Level ) = 0;
      virtual HRESULT __stdcall get_Attributes (
        /*[out,retval]*/ long * plAttr ) = 0;
      virtual HRESULT __stdcall put_Attributes (
        /*[in]*/ long plAttr ) = 0;
      virtual HRESULT __stdcall get_CursorLocation (
        /*[out,retval]*/ enum CursorLocationEnum * plCursorLoc ) = 0;
      virtual HRESULT __stdcall put_CursorLocation (
        /*[in]*/ enum CursorLocationEnum plCursorLoc ) = 0;
      virtual HRESULT __stdcall get_Mode (
        /*[out,retval]*/ enum ConnectModeEnum * plMode ) = 0;
      virtual HRESULT __stdcall put_Mode (
        /*[in]*/ enum ConnectModeEnum plMode ) = 0;
      virtual HRESULT __stdcall get_Provider (
        /*[out,retval]*/ BSTR * pbstr ) = 0;
      virtual HRESULT __stdcall put_Provider (
        /*[in]*/ BSTR pbstr ) = 0;
      virtual HRESULT __stdcall get_State (
        /*[out,retval]*/ long * plObjState ) = 0;
      virtual HRESULT __stdcall OpenSchema (
        /*[in]*/ enum SchemaEnum Schema,
        /*[in]*/ VARIANT Restrictions,
        /*[in]*/ VARIANT SchemaID,
        /*[out,retval]*/ struct _Recordset * * pprset ) = 0;
};
 
struct __declspec(uuid("00000550-0000-0010-8000-00aa006d2ea4"))
_Connection : Connection15
{
    //
    // Raw methods provided by interface
    //
    virtual HRESULT __stdcall Cancel ( ) = 0;
};
 
_Connection接口继承了Connection15接口,Connection15 又继承了_ADO。_ADO接口是绝大多数ADO接口的基接口。
 
使用_Connection接口执行SQL语句
CComBSTR SQL(L"DELETE FROM [15]");
hr=pCon->Execute(SQL,NULL,adCmdText|adExecuteNoRecords,NULL);
Execute也可以执行查询语句返回结果记录集,第三个参数取消adExecuteNoRecords第四个参数将_Recordset的指针地址传递进来即可。
 
MDAC2.0开始引入了连接池的概念,ODBC中使用了connection pooling,OLEDB和ADO中使用了resource pooling。
ADO中,如果连接字符串相同,但是动态属性值不一样(比如:PROMPT),MDAC仍然会创建两个用户身份。连接池的用户身份和操作系统的登陆账号不是一回事,它取决于连接字符串和任何在连接对象打开之前的设置的属性值。如果客户程序使用同样的用户身份和连接属性对同一个数据存储请求连接对象,连接池中的合适的连接对象将被激活。连接对象被释放后会返回连接池中,经过60秒不被激活,会被自动销毁。
ADO的连接对象实际上内部使用数据源代理对象,如果在连接对象关闭状态下设置连接对象的属性,则属性值将缓存在本地的数据源代理对象中。当打开连接对象时会检查resource pooling中是否有和代理请求的相同的数据源代理对象,如果有则使用池中的,否则创建。所有resource pooling只存放数据源代理对象,不会存放其他的资源。MDAC2.5版本之前不可以修改resource pooling中的数据源代理对象实效时间。
ADO默认会启用resource pooling。但是如果客户程序不能保持至少一个连接对象,则
连接池不会生存。这里可以考虑使用COM+对象池保存连接对象。
       打开的连接对象一定要通过Close关闭,不要忘记,否则连接对象不能返回resourse pooling中。将导致性能降低。
       不过最终决定不再使用连接对象,应该调用Release方法释放。这是COM的规则。
       _Connection::put_ConnectionString方法用来设置连接字符串,但是注意以下几点:
1)_Connection::Open打开时如果第一个参数非空,该参数也可以设置或者覆盖ConnectionString,
2)如果使用UDL文件,如前面所举的例子,_Connection::get_ConnectionString获取到的不是UDL文件名,而是UDL文件的属性,比如:
“Provider=SQLOLEDB.1;
Password=sa;
Persist Security Info=True;
User ID=sa;
Initial Catalog=ff;
Data Source=FREEBIRD;
Use Procedure for Prepare=1;
Auto Translate=True;
Packet Size=4096;
Workstation ID=FREEBIRD;
Use Encryption for Data=False;
Tag with column collation when possible=False“
3)连接字符串使用”参数=参数值;”方式构成,ADO支持五种参数,其他由数据提供者提供:Provider 、File Name、Remote Provider、Remote Server、URL。ADO只是将其他参数直接传递给数据提供者,自己并不做任何处理。
 
 
COM+对象池保存Connection对象
       近一年多来我本人几乎从来不在客户程序中调用ADO,这与从事的编程工作多为系统开发有关。系统开发讲究服务器资源的负荷不能超过极限,讲究客户调用服务快进快出等。在微软的平台上开发DNA系统一定要借助于COM+。所以我重点讨论COM+中如何运用ADO编程,这就是本文与一般的ADO文章的最大不同。
我将在COM+对象CMIS类中维护一个局部变量CComPtr<_Connection> m_pCon;我准备将该COM+对象配置为支持对象池和JITA。关于COM+对象池和JITA可以参考我的另一篇文章《COM+编程研究之对象池、JITA》,这里不做说明。
由于_Connection接口指针每次当方法调用后会被放入对象池中,而下一次调用它的客户将来自另一个COM+上下文,所以必须经过轻量列集,而不能直接使用m_pCon,所以我们需要
1) 在GIT中保存_Connection接口;
2) 在每次方法调用时从GIT中散集该接口;
3) 在每次方法调用后释放该接口;
4) 在最后对象被释放的时候要将GIT中保存的接口指针删除。
请参考下面的源代码示例:
创建Connection对象,并将获取的_Connection接口指针保存到GIT中,m_Cookie用来以后从GIT中取出该接口指针。
HRESULT FinalConstruct()
     {
         HRESULT hr=m_pCon.CoCreateInstance(__uuidof(Connection));
         if(hr!=S_OK)
              return hr;
         hr=m_pCon->put_ConnectionString(CComBSTR(L"File Name=c:\\海塘.udl"));
         if(hr!=S_OK)
              return hr;
         hr=m_pCon->Open(L"",L"",L"",NULL);
         if(hr!=S_OK)
              return hr;
         CComPtr<IGlobalInterfaceTable> pGTable;
         hr=pGTable.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
         if(hr!=S_OK)
              return hr;
         hr=pGTable->RegisterInterfaceInGlobal(m_pCon.p,__uuidof(_Connection),&m_Cookie);
         m_pCon.Release();
         return hr;
     }
方法调用时从GIT中列集_Connection指针:
HRESULT CMIS::Activate()
{
     HRESULT hr = GetObjectContext(&m_spObjectContext);
     if (hr!=S_OK)
         return hr;
    
     CComPtr<IGlobalInterfaceTable> pGTable;
     hr=pGTable.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
     if(hr!=S_OK)
         return hr;
     hr=pGTable->GetInterfaceFromGlobal(m_Cookie,__uuidof(_Connection),(void**)&m_pCon.p);
     if(hr!=S_OK)
         return hr;
     return S_OK;
}
 
设定方法调用后总是返回到对象池
BOOL CMIS::CanBePooled()
{
     return TRUE;
}
方法调用后将当前_Connection接口释放
void CMIS::Deactivate()
{
     m_pCon.Release();
     m_spObjectContext.Release();
}
 
我们如何让我们的COM+对象CMIS总是拥有至少一个ADO连接对象呢?很简单,将COM+对象池下限设为1。
列集_Recordset接口指针到客户程序:
       话题才刚刚开始。我打算用以下方法进行测试,CMIS对象提供一个方法GetShuJuZiDian从ff数据库中的数据字典表中读入所有数据,该方法将把_Recordset接口指针传递出去,客户需要自行导入ADO15.DLL中的类型库信息才能使用。该_Recordset是从Connection对象的Execute方法获取的,因此是只读并且向前移动的,这样正好,因为我不想让客户获得过大的权力。
       IDL如下:
interface IMIS : IDispatch{
     [id(1), helpstring("方法将传递一个_Recordset接口指针,传递出去的记录集对象是只读并且只能向前移动")] HRESULT GetShuJuZiDian([out]IUnknown** pRec);
};
       代码实现如下:
STDMETHODIMP CMIS::GetShuJuZiDian(IUnknown** pRec)
{
     //The returned Recordset object is always a read-only, forward-only cursor.
     CComBSTR SQL(L"select * from 数据字典表");
     HRESULT hr=m_pCon->Execute(SQL,NULL,adCmdText,(_Recordset**)pRec);
     return hr;
}
 
       客户端调用代码如下:
#include "stdafx.h"
#import "haitangmis.tlb" no_namespace raw_interfaces_only named_guids
#include <iostream>
using namespace std;
#include <atlstr.h>
int _tmain(int argc, _TCHAR* argv[])
{
     ::CoInitialize(NULL);
     CComPtr<IMIS> pMIS;
     HRESULT hr=pMIS.CoCreateInstance(CLSID_MIS);
     IUnknown* pIUn=NULL;
     hr=pMIS->GetShuJuZiDian(&pIUn);
     VARIANT_BOOL vResult;
     CComPtr<_Recordset> pRec;
     hr=pIUn->QueryInterface(__uuidof(_Recordset),(void**)&pRec.p);
     hr=pRec->get_ADOEOF(&vResult);
     CComPtr<Fields> pFields;
     CComPtr<Field> pField;
     CComVariant Value;
     while(vResult==VARIANT_FALSE)
     {
         hr=pRec->get_Fields(&pFields.p);
         hr=pFields->get_Item(CComVariant(0),&pField);//
         CComBSTR Name;
          pField->get_Name(&Name);
         cout<<CString(Name)<<endl;
         hr=pField->get_Value(&Value);
         hr=Value.ChangeType(VT_BSTR);
         cout<<CString(Value.bstrVal)<<endl;
         hr=pRec->MoveNext();
         pField.Release();
         pFields.Release();
         hr=pRec->get_ADOEOF(&vResult);
     }
 
     hr=pRec->Supports(adMovePrevious,&vResult);
     hr=pRec->Supports(adUpdate,&vResult);
     pRec.Release();
     pIUn->Release();
     pMIS.Release();
     ::CoUninitialize();
     int x;
     cin>>x;
     return 0;
}
 
 
                                                                                           freebird
5/16/2005

分享到:
评论

相关推荐

    Visual c++编程宝典.pdf

    根据提供的信息,“Visual C++编程宝典.pdf”这一资料涵盖了丰富的C++编程知识与实践案例。下面将基于标题“Visual C++编程宝典.pdf”以及描述中的“包含了很多知识包括PDF等,内容详细”这两部分信息,来总结并展开...

    ----Delphi编程宝典+编程手册----

    "delphi编程手册 V3"则可能侧重于提供实用的参考信息,如: 1. **API函数**:详细列出Windows API函数的用法,为需要进行底层操作的开发者提供指导。 2. **组件参考**:详尽的VCL组件文档,包括每个组件的属性、...

    Visual C++ 6编程宝典 pdf格式电子书,中文扫描

    总的来说,《Visual C++ 6编程宝典》是一本全面而实用的教程,对于想要掌握Visual C++ 6和MFC技术的开发者来说,是一份宝贵的参考资料。通过阅读这本书,读者不仅可以了解MFC的基本用法,还能深入理解Windows程序...

    软件编程宝典电子书之21天学会c c++ java vc asp.net VB 等超实用电子书exe

    《软件编程宝典电子书之21天学会C C++ Java VC ASP.NET VB等超实用电子书》是一本全面涵盖多种编程语言的综合教程,旨在帮助初学者在短时间内掌握基础编程技能。这本电子书的精华在于其系统性和实用性,通过21天的...

    Delphi 编程宝典

    6. **数据库编程**:使用ADO或DBExpress等技术进行数据库连接和数据操作。 7. **网络编程**:利用Indy或其它库实现网络通信,如HTTP、FTP、SMTP等协议的使用。 8. **异常处理**:学习如何正确地捕获和处理程序运行时...

    Delphi编程宝典

    《Delphi编程宝典》是一本深度探讨Delphi编程技术的专著,旨在为开发者提供全面、实用的指导。Delphi作为一款强大的Object Pascal开发工具,因其高效、易用和跨平台特性,一直以来深受程序员的喜爱。它不仅拥有直观...

    Delphi之葵花宝典

    本宝典以其丰富的内容和实用的指南,成为了Delphi程序员不可或缺的学习工具。 在《Delphi之葵花宝典》中,你可以找到涵盖以下多个方面的知识点: 1. **基础语法**:包括Delphi的基本数据类型、变量、常量、运算符...

    Visual C++程序开发范例宝典(PDF扫描版).part2

     cc实例206 用ADO动态连接数据库   cc实例207c连接Oracle数据库   8.2 添加数据   cc实例208 利用INSERT语句批量插入数据   cc实例209 利用SELECTcINTO生成临时表   8.3 更新数据   cc实例210 ...

    Delphi葵花宝典

    4. **数据库编程**:Delphi提供了强大的数据库访问能力,书中的数据库章节涵盖了ADO(ActiveX Data Objects)、BDE(Borland Database Engine)以及后来的FireDAC(Firebird Database Access Components)的使用,...

    C#程序开发范例宝典源码

    "案例"是学习编程的实用部分,它们展示了如何将理论知识应用于实际问题解决。例如,可能会有创建简单的Windows Forms应用程序的案例,展示如何设计用户界面,处理用户输入;也可能会有ASP.NET Web应用程序的案例,...

    Visual C++程序开发范例宝典(光盘) 第八部分

    Visual C++程序开发范例宝典配套光盘,因大小受限,所以分成8部分上传,必须全部下载才能正常解压! 第1章 窗体与界面设计 1.1 菜单应用实例 实例001 在系统菜单中添加菜单项 实例002 带图标的程序菜单 实例003...

    VB6.0程序设计范例宝典

    《VB6.0程序设计范例宝典》是一本针对初学者和有一定基础的VB6.0程序员编写的实用教程,旨在通过丰富的实例帮助读者掌握Visual Basic 6.0编程的核心技术和技巧。这本书包含了完整的源代码,使得学习者能够直接运行、...

    VB程序开发范例宝典第一部分

    第二部分可能进一步讲解了VB的高级特性,比如错误处理、模块化编程、用户界面设计(如Form控件、菜单、对话框等)、文件操作、数据库访问(如ADO.NET)等。这些内容让开发者能够创建功能更丰富、交互性更强的应用...

    c#实战宝典部分案例(共5个)

    例如,使用ADO.NET进行数据库连接,执行SQL查询,读取和写入数据,这对于开发数据驱动的应用程序至关重要。 此外,C#的GUI编程也是案例中不可或缺的部分。可能涵盖Windows Forms或WPF,用于创建用户友好的图形界面...

    delphi开发宝典

    《Delphi开发宝典》是一本专...总的来说,《Delphi开发宝典》不仅提供了一套系统的Delphi学习路径,还通过丰富的示例代码和实用的资源,帮助读者深入理解Delphi的数据库和网络编程,是学习和提升Delphi技能的宝贵资料。

    DELPHI技术方案宝典

    总的来说,"DELPHI技术方案宝典"是一份宝贵的资源,无论你是初学者还是经验丰富的开发者,都能从中获取大量实用的知识和技巧,提升你的DELPHI编程技能。通过系统地学习和实践,你将能够熟练地运用DELPHI构建高效、...

    VB技术方案宝典光盘全部内容

    5. **ADO.NET数据访问**:VB可以方便地使用ADO.NET进行数据库操作,这包括连接数据库、执行SQL语句、处理结果集等。 6. **集成开发环境(IDE)**:VB的IDE提供了代码编辑、调试、编译和部署等一系列功能,为开发者...

    《Delphi编程技巧集》Delphi编程技巧大全

    《Delphi编程技巧集》是一本全面探讨Delphi编程技巧的资源宝典,旨在帮助开发者提升在使用Delphi进行软件开发时的效率和代码质量。Delphi,作为一款强大的RAD(快速应用程序开发)工具,以其高效的VCL框架和对对象 ...

    asp 最全最实用的速查宝典

    这份"asp 最全最实用的速查宝典"显然是一份全面且实用的ASP技术参考资料,包含了各种ASP编程中的常见技术和问题解决方案。下面将详细讨论其中可能涵盖的知识点。 1. **基础语法与结构**:ASP文件以.asp为扩展名,...

    Delphi开发经验技巧宝典随书光盘

    《Delphi开发经验技巧宝典》是一本专为Delphi开发者设计的实用指南,它包含了丰富的编程技巧和实践经验,旨在帮助读者提升在Delphi环境下的软件开发能力。这本书的随书光盘,如文件名所示,包含了一份isz格式的主...

Global site tag (gtag.js) - Google Analytics