吃桃子还是苹果

我吃桃子

 

ADO数据库

在用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);

Update,Insert,Delete:
     当我进行Update,Insert,Delete操作时,通常我喜欢用Connection和Command对象
,原因是,用CString来构造SQL语句简单一些,然后直接用Connection.Execute就行了
。当然,用Recordset干这些也是可以的。

     Update方法有下面三种方法调用:
     1: 给某个Field对象(或某些个)的Value属性赋值,然后调用Update方法;
     2: 将字段名和字段值作为参数传给Update方法;
     3: 将字段名和字段值的数组作为参数传给Update方法。

     AddNew方法如下调用:
     1: 直接调用,然后同Update调用方法一;
     2: 将字段名数组、字段值数组作为参数传给AddNew方法。

     Delete方法最简单,直接调用就行了,删除当前记录!

     做完这些事情,可能需要调用Requery方法才能看到效果。
     下面的示例代码需要我们创建一个简单的MFC Application,然后在CWinApp类里面
声明三个接口的对象:

     // Global ADO Objects
     // connection
     _ConnectionPtr m_pConnection;
     _CommandPtr   m_pCommand;
     _RecordsetPtr m_pRecordset;

     在VC6里面有一点很有意思,如果敲 "m_pConnection.",你会看到一个方法和属性
列表,如果敲"m_pConnection->",还会看到一个方法和属性列表,当然里面的内容完全
不同,因为你实际是在指向两个不同的东西。下面就是这两种混用的代码:

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

     回到示例代码,在 application 的 InitInstance 方法里,我打开数据连接,指向我
机器上的一个数据库,你需要更改ConnectionString,使用你自己的ODBC数据源或指定
一个OLE DB provider。

     // When we open the application we will open the ADO connection
     m_pConnection.CreateInstance(__uuidof(Connection));
     m_pConnection->Open("DSN=ADOTest","","",-1);

     如果你打开about对话框,就会看到一个Listbox,还有一个叫button1的按钮,这里
面包含了 ADO 调用的核心代码。我创建了一个_RecordsetPtr接口的实例,打开我需要
的记录集,然后遍历所有记录,将它们塞到Listbox里去:

     _variant_t TheValue;
     theApp.m_pRecordset.CreateInstance(__uuidof(Recordset));
     try
     {
         theApp.m_pRecordset->Open("SELECT DISTINCT FLDESC FROM tblFALines",
                 theApp.m_pConnection.GetInterfacePtr(),
                 adOpenDynamic,
                 adLockOptimistic,
                 adCmdText);
         while(!theApp.m_pRecordset->adoEOF)
         {
                 TheValue = theApp.m_pRecordset->GetCollect("FLDESC");
                 if(TheValue.vt!=VT_NULL)
                 m_List.AddString((char*)_bstr_t(TheValue));
                 theApp.m_pRecordset->MoveNext();
         }
         theApp.m_pRecordset->Close();
     }
     catch(_com_error *e)
     {
         CString Error = e->ErrorMessage();
         AfxMessageBox(e->ErrorMessage());
     }
     catch(...)
     {
         MessageBox("Whoa this is bad");
     }

     记得一定要用try和catch,否则ADO调用错误有可能使你的程序崩溃,一定要随时记
得捕捉_com_error例外以及其它错误。
     我尽可能的使代码简单,所以省略了很多细节,尤其是忽略了很多好的编程习惯(比
如检查大多数COM方法都返回的HRESULT值)。本文的目的是想说,ADO并没什么难的,CO
M也一样,而不是想表现ADO能做的所有事情。我甚至都没仔细想过ADO能为你带来什么,
不过我肯定一点,那就是ADO比DAO、RDO更快、更容易使用、并且功能强大得多。看看本
站点其它的文章,你就会知道,通过ADO调用存储过程有多么容易!
     最后,我想向大家推荐两本书,其中有一本是完全免费的,在www.informit.com可
以找到电子版,另一本必须得付钱买了,不过我还是推荐两本都买,除非你家浴池里、
床头边也放了计算机。 :)
     免费的那本是<Learn Database Programming with Visual C++ in 21 days>。哦,
我知道你在想什么,我也知道那些'in 21 days'的书通常都很烂,而且当着其它程序员
的面买这样的书的确有点丢面子,并且还很不好意思把这样的书摆在书架上显眼的位置
。不过这一本绝对是个例外!里面的内容简直太棒了!
     要花钱的那本是<ADO 2.0>,由WROC出版... (译者注:细节就省了吧,反正我们也
不会花美元去买英文书)。

posted on 2008-09-26 14:41 你弹我唱 阅读(515) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

常用链接

留言簿(1)

随笔档案(12)

文章档案(2)

控件

友情链接

指针

搜索

最新评论

阅读排行榜

评论排行榜