ADO is AOK (ADO其实很简单)

翻译改写:wesley at 水木清华。 Email: wesley@video.mdc.tsinghua.edu.cn。

前言:
    有人经常问:现在最好的数据访问方法是什么?回答当然是ADO!
    M$花了不少时间,推出了一种叫 UDA(Universal Data Access)的东东,还有一套看
起来蛮简单的数据访问对象ADO(ActiveX Data Object),用来替代过时的 DAO(Data Ac
cess Object)、RDO(Remote Data Object)。
    DAO 的底层是 JET 引擎,主要用来提供对 ACCESS 数据库的访问,比较新的版本也
支持访问其他数据库,不过对于其他数据库,需经过 JET 的中间层,访问速度比较差。
在所有对 ACCESS 数据库的访问方法中, JET是最快的。最新的 JET Engine版本为4.0
,对应的 DAO 版本为3.6,可以访问 ACCESS 2000 的数据库。MFC里面 CDAODatabase 
和 CDAORecordset 即为 DAO 的 MFC 包装。
    RDO 的底层是 ODBC,RDO仅仅是对ODBC API的一个薄包装层,薄得简直有点不象样
,很多人都用 ODBC API写过程序 (我的第一个SQL Server客户端程序就是用ODBC API写
的一个Console Application),把那些并不复杂的API跟RDO一比就能发现,包装得简直
没有专业精神。也正因为薄,所以速度较快,在ADO出现以前,访问MS SQL Server最快
的方法就是 RDO 了(不要跟我说还有DB-Library,不论是看MS的文档,还是我亲自实验
,DB-Library都没有ODBC/RDO快)。最新的 RDO 版本为 2.0,说是最新,似乎也有好几
年没有更新了,原因是MS早已经决定将其淘汰。不过遗憾的是,现在大家都还在用的 C
Database、CRecordset 就是 RDO 的 MFC 包装。现在还用这两个类,就象有了宝马奔驰
,还骑破永久上下班,只是因为不会开车。 :-(
    ADO 的底层是 OLE DB,不仅能访问关系型数据库,也可以访问非关系型数据库,这
可是现在最快速的数据库访问中间层啊!ADO对OLE DB的包装可以说相当成功,对象模型
简明扼要,没有一点多余的东西,功能还远超DAO、RDO。直到此时,我才算是有点佩服
MS,OLE DB里面数十个密密麻麻的接口对我来说实在是太恐怖了,还是乖乖的用ADO吧。

    有一点很不幸,微软提供的ADO文档几乎没有有关VC的内容,象我这样的VC菜鸟,坐
进了宝马舒适的驾驶仓,不知道该怎么上手下脚,郁闷之极!
    这篇文章真是救黎民于水火之中,不容我不把它贡献出来让大家共享。

开始:
    在用ADO以前,一定得让你的程序知道去哪里找ADO。在stdafx.h文件里,需要加上
下面的代码:
 #import "c:/program files/common files/system/ado/msado15.dll" no_namespace
s rename("EOF", "adoEOF")
    这行代码的作用是,告诉编译器去哪里找ADO的库文件(可能在你的机器上路径有所
不同),然后说明不用namespace,并且将 EOF 更名为 adoEOF(如果不这样干,很有可能
会碰到常量冲突)。
    只要加了这句话,准备工作就全干完了,很简单是吗?不用包含任何头文件,不用
为link指定任何lib文件,我也觉得有点神奇。//shrug

_ConnectionPtr, _CommandPtr, 和 _RecordsetPtr (本文中未提及 _CommandPtr):
    ADO,和 CDAODatabase、CDatabase 非常相似,也分这么几块,不同的是,ADO 以
 COM 为基础,这几块都是标准的COM组件,而 CDatabase 等等则是 MFC 类。有一点必
须提请注意,要学习ADO编程,学点COM是不可避免的了,不过这是件好事,现在如果不
会一点COM、OLE什么的,估计很难适应Windows编程的形势。ADO里面的三个组成部份就
是三个COM组件:Connection、Command、Recordset。(还有两个暂时用不上的:)
    Connection用于建立数据库连接,执行不返回任何结果集的SQL语句。
    Command用于返回结果集,并提供简单的方法执行存储过程或者任何返回结果集的S
QL语句。
    Recordset就是结果集,可进行数据的存取、滚动操作。
    如果给Command和Recordset正确的Connection string,而不是一个Connection对象
的指针,它们一样可以打开记录集,这种情况适用于单数据库操作。当程序里需要频繁
进行数据库操作时,最好还是预先定义一个Connection对象,用它连接数据库,而用Re
cordset处理数据。本文中将大量讨论这两个对象。
    _ConnectionPtr是一个Connection的接口,与CDatabase和CDAODatabase类似,实际
工作原理也差不多。在程序里创建它的实例,通过某个OLE DB provider指向一个数据源
,并开启连接。下面的代码是CDAODatabase和_ConnectionPtr的开启实例:

DAO:
    CDaoDatabase MyDb = new CDaoDatabase();
    m_DaoServerDB.Open(NULL,FALSE,FALSE,"ODBC;DSN=SAMS_SVR;UID=admin;PWD=adm
in");

ADO:
    _ConnectionPtr MyDb;
    MyDb.CreateInstance(__uuidof(Connection));
    MyDb->Open("DSN=SAMS_SVR;UID=admin;PWD=admin","","",-1);

    _RecordsetPtr是记录集接口,与CDAORecordset类似。先看看它的开启方式与CDAO
Recordset有多么相似:

DAO:
    CDaoRecordset MySet = new CDaoRecordset(MyDb);
    MySet->Open(AFX_DAO_USE_DEFAULT_TYPE,"SELECT * FROM some_table");

ADO:
    _RecordsetPtr MySet;
    MySet.CreateInstance(__uuidof(Recordset));
    MySet->Open("SELECT * FROM some_table", MyDb.GetInterfacePtr(), adOpenDy
namic, adLockOptimistic, adCmdText);

    (译者注:请注意ADO在Open Recordset的时候,使用了MyDb.GetInterfacePtr()作
为参数之一,当时我用了_ConnectionPtr, &(_ConnectionPtr)都不行,原来要这么用,
真是不服不行。:()
    ADO只是略微的麻烦一点,不过花这点功夫获得ADO的多多好处还是很值的。
    现在有了一个Connection和一个Recordset,下面该用这两个东东取点数据出来了。
不妨先假定有一个名为m_List的Listbox,我们把数据取出来往里塞。

DAO:
    VARIANT *vFieldValue;
    COleVariant covFieldValue;
    CString Holder;
    while(!MySet->IsEOF())
    {
        MySet->GetFieldValue("FIELD_1", covFieldValue);
        vFieldValue = (LPVARIANT)covFieldValue;
        if(vFieldValue->vt!-VT_NULL)
        {
            Holder.Format("%s",vFieldValue->pbVal);
            m_List.AddString(Holder);
        }
        MySet.MoveNext();
    }

ADO:
    _variant_t Holder
    while(!MySet->adoEOF)
    {
        Holder = MySet->GetCollect("FIELD_1");
        if(Holder.vt!=VT_NULL)
        m_List.AddString((char*)_bstr_t(Holder));
        MySet->MoveNext();
    }

    注意:在微软所有的文档里,都没找到GetCollect方法。我找了所有的地方,也从
没人提起过,在文档里的示例方式是这样:
        Holder = MySet->GetFields->Field->(_variant_t(FieldNumber))->Value;
    你喜欢哪一种呢,反正我是喜欢GetCollect。(译者注:鬼知道他从哪里搞到这个方
法,难道是从MS自己人的程序里?I 服了 him。)

动态绑定 vs DFX(CRecordset 和 CDAORecordset的预先字段绑定,Data Field Exchan
ge):
    动态绑定即使用SQL语句动态构造结果字段,而不是象 CDAORecordset 里面用DFX去
把所有原始字段映射成成员变量。动态绑定的例子:
    SELECT (SUM(field_1) + SUM(field_2)) AS answer FROM some_table
    如果用DFX,估计就得在程序里自己写了:
    m_answer = m_field_1 + m_field2;
    (译者注:尽管很多人喜欢 DFX 的字段预先绑定,宁可这么写代码,不过我很少用
 DFX,当然在 VB 里面更是从来不会用到,好象用得比较多的也就是在 Delphi 里面,
用起来还比较爽,大概是 MS 的文档里让我别用,而 Borland 的破文档里却没说吧。 
:-)
    相比之下,动态绑定确有其优越的地方,减少了代码量,也让程序更小、更易维护
。再说,这也是MS推荐的获取数据方式,更灵活,速度更快,更易维护,我们还能要求
什么呢?
    对于大多数程序来说,都是创建一个全局的Connection,然后用Recordset来处理数
据,如果Recordset数量很多,可以想象光是花费在DFX上面的代码就有多少。如果使用
动态绑定,这些都省掉了。

_variant_t 和 _bstr_t 到底是什么玩意?
    很不幸,我们喜爱的CString类在COM里用不了(CStringEx也一样),因为COM必须设
计成跨平台,它需要一种更普遍的方式来处理字符串以及其他数据。这就是VARIANT数据
类型的来历,还有BSTR类型。VARIANT就是一个巨大的 union,包含了你能想得到的所有
的数据类型,除了char*,不过还好,BSTR取代了char*。
    (译者注:似乎VARIANT是个很慢的东西,大家都不愿意使它,不过按我看来,情况
没这么糟糕,union照理说不应该慢到哪去,要说慢,也是慢在给VARIANT分配地址空间
上,这点在VC里面做得比VB要好
    这些东西看起来的确有点恐怖,不过实在用不着怕,等下面熟悉了这两个东西之后
,你会很快喜欢的)
    简单来说,_variant_t是一个类,包装了VARIANT数据类型,并允许我们简单的对之
进行强制类型转换(相信大家都喜欢这个),_bstr_t对BSTR干了同样的事情。在下面的例
子里,你将看到怎么用GetCollect把数据取到VARIANT里,又怎么把它放到_bstr_t里,
最后强制转换成char*,以及把_variant_t强制转换成long、double或者其他一切东西:

    _variant_t Holder;
    // first get the VARIANT and put it into the _variant_t
    Holder = MySet->GetCollect("FIELD_1");
    // now put it into a _bstr_t and cast it to a char*
    m_List.AddString((char*)_bstr_t(Holder));

    对比一下没有用 _variant_t 和 _bstr_t 的代码:

    COleVariant covFieldValuel
    VARIANT vFieldValue
    CString Holder;
    MySet->GetFieldValue("FIELD_1", covFieldValue);
    vFieldValue = (LPVARIANT)covFieldValue;
    Holder.Format("%s",vFieldValue->pbVal);
    m_List.AddString(Holder);

    区别大了!

(待续)

--
世界上既无所谓幸福也无所谓不幸
只有一种状况和另一种状况的比较
只有体验过极度不幸的人
才能品尝到极度的幸福
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值