精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品开源心得,转载请注明:“锐英源www.wisestudy.cn,孙老师作品,电话13803810136。”需要全文内容也请联系孙老师。英文原文
ADO使用OLEDB提供者来操纵和存储数据,且因为它提供了双向接口,你能在脚本语言里使用它,比如VBScript和JavaScript,也可以在C++语言里使用。它也有很多其它优点,比如高速,低内存消耗,最重要的是,它易于使用。
在这个简短的文章里,我将会描述出怎样开始使用ADO,比如,怎样打开记录集recordset,查询数据库或者执行存储过程。
先说一下前提条件。本文所设定环境如下:例子数据库ADOTestDB.MDB,VC++ 6 (SP3)和ADO 2.1。在数据库里,只有两个简单的表,名称为Student和Dept,它们的字段和类型各不一样,也有一些例子数据。使用import指令来获取ADO的智能指针特性。
在你的stdafx.h文件里输入如下代码:
#import "msado15.dll" \ no_namespace \ rename( "EOF", "adoEOF" )
把EOF重命名为adoEOF,来防止你的应用程序受某些讨厌的冲突干扰。
上面代码添加过后,你需要初始化COM来使用ADO。为了实现初始化,调用CoInitialize(NULL)函数,调用放到你代码开始的位置,在使用过ADO之后,记得调用CoUninitialize()。
存取数据库的第一步是连接到数据源。为了连接到数据源,你要使用ADO的Connection对象。ADO建立连接的主要办法是通过Connection的Open成员函数。浏览一下下面的代码(为了代码简洁,错误处理忽略了):
_ConnectionPtr m_pConn; m_pConn.CreateInstance (__uuidof(Connection)); m_pConn->Open ( _bstr_t ( "Provider=Microsoft.Jet.OLEDB.4.0; Data Source = ADOTestDB.MDB" ), _bstr_t ( "" ), _bstr_t ( "" ), adModeUnknown );
T这段代码是通过Microsoft.Jet.OLEDB.4提供者建立到ADOTestDB.MDB数据库连接的代码片段。首先,你必须定义一个_ConnectionPtr(ADO Connection)类型的对象,接着你初始化它,最后调用Open函数。Open函数的第一个参数是连接字符串,这个参数指明了你尝试连接的数据源和OLEDB提供者。随后两个参数指明UserName和Password来登录进入到数据库。我们的例子数据库不需要任何用户,所以你能用空字符串,最后一个参数是选项Options,它决定了打开是异步或者同步的。如果你不知道这个特性,你能简单地用adModeUnknown。
使用ODBC驱动来连接数据源也是一个不错的想法,如下的代码显示了怎样使用它:
m_pConn->Open (_bstr_t ("DSN=pubs;uid=sa;pwd=;"), _bstr_t (""), _bstr_t (""), adCmdUnknown );
在ADO内,有不同的办法来构造SQL语句。最常用的办法是使用Command对象。Command是一个指令,数据提供者能够解释这个指令来修改管理和操纵数据源,这些指令以SQL格式编写。现在,我们要query查询Student表里的所有学生数据。代码如下:
_CommandPtr pCommand; pCommand.CreateInstance (__uuidof (Command)); pCommand->ActiveConnection = m_pConn; // Formerly opened connection // pointer pCommand->CommandText = "Select * From Student";
如同你看到的,首先,我们声明一个Command对象,接着初始化它。随后,我们设置ActiveConnection属性为先前打开的连接对象,m_pConn。CommandText属性表示查询、SQL语句或者存储过程名称,它会被提供者处理,对于我们的使用来说,它就是一个简单的查询,"Select * From Student"。
当我们的命令文本查询时,通过处理,它会返回一个行的集合,我们应该把这个集合存储到某些地方。 了实现此目的,我们定义了一个Recordset对象,它会存储返回的记录行,且让你能够操纵这些记录行。
现在,我们将要执行这个命令,但是我要提醒的是。有两个办法来执行ADO的命令,第一个是通过Command的Execute成员函数,另外一个是Recordset的Open成员函数。这里我们通过Recordset的Open来实现:
_RecordsetPtr pRecordset; pRecordset.CreateInstance (__uuidof (Recordset)); pRecordset->CursorLocation = adUseClient; pRecordset->Open ( (IDispatch *) pCommand, vtMissing, adOpenStatic, adLockBatchOptimistic, adCmdUnknown);
在这段摘抄的代码里,我们先定义一个Recordset类型的对象,接着初始化它。在ADO里,Recordset对象有几十个属性,各有各的目的,我们使用的属性是CursorLocation,它让你指定在客户端和服务器端之间的游标的位置。属性设置过后,你能调用Open函数来处理对应数据源的命令。Open函数的第一个参数指定了一个变体变量(其值为Command对象、SQL语句、表名、存储过程或者一个被理解为源Source的URL)。第二个参数是一个活动的连接对象,是个可选参数。由于我们在Command对象里指定连接,这里就不需要连接参数了,只是简单地忽略参数(用值为DISP_E_PARAMNOTFOUND 类型为VT_ERROR 的变体变量_variant_t 来表示,也可用变体变量的常量vtMissing 来表示,vtMissing是由 #import指令引入的)。第三个和第四个参数是游标类型和锁定类型。锁定类型为adLockBatchOptimistic,这是因为我们要用到批处理,且因为这个处理需要游标服务cursor services ,我们在前面指定游标位置。如果有不同于Command对象的别的情况,最后的参数指示提供者应该怎样评价源Source参数,但是我们的源Source恰好是一个command对象,所以简单地设置最后参数是adCmdUnknown。现在,在调用了Open函数后,你就有了含有所有学生信息的Recordset对象。
这里,我们将修改一些数据。在Student表里有一个字段SocialSecNo,这个字段显示每个学生社会安全号码,它必须从类似’45’开头的修改为以’77’开头。所以我们必须修改所有的以’45’开头的记录到’77’开头的形式。为了实现这个任务,我们为以’45’开头的SocialSecNo来进行过滤,而且,我们将设置StudentNo字段做为记录集的索引,以使排序和过滤性能提高。
此时你可能想,以上面所说方法来实现如下要求可能效率低,且依赖于情形有可能是正确的,但我的最重要的目的是,介绍ADO的不同能力,所以不要考虑效率的事情,高高兴兴用ADO的特性。
操纵数据的代码如下:
pRecordset->Fields->GetItem ("StudentNo")->Properties-> GetItem ("Optimize")->Value = VARIANT_TRUE; pRecordset->Sort = "Name"; pRecordset->Filter = "SocialSecNo LIKE '45*'"; while (!pRecordset->GetadoEOF()) CString str = (char *) (_bstr_t) pRecordset->Fields-> GetItem("SocialSecNo")->Value; str = "77" + str.Right(str.GetLength() - 2); pRecordset->Fields->GetItem("SocialSecNo")->Value = (_bstr_t) str; pRecordset->MoveNext(); } pRecordset->Filter = (long) adFilterNone;
在上面代码里,我们设置StudentNo成员的Optimize属性为真来使它为记录集的索引,接着,我们以Name字段来排序,以’45’开头的SocialSecNo字段为过滤。在while循环里,我们只简单地修改SocialSecNo值来新值,并向下移动记录集的游标指针。当你修改了SocialSecNo字段为新值后,它的新值不再匹配过滤条件,所以它对记录集不可见。为了记录集的重新表现,我们应该在最后删除过滤器。
在ADO里有两种办法来更新数据。第一个方法是立即更新,它意味着一旦你调用Update函数,你对记录集直接实现了修改,且由此影响了数据源。但是第二个办法被认为是Batch模式更新。如果你以adLockBatchOptimistic锁定类型打开记录集,ADO让你以Batch模式来更新数据。在Batch模式下,对记录的移动或者调用Update函数在一个拷贝缓冲里实现更新,且只在调用了BatchUpdate函数后,修改才被通知到数据源里。
在我们的例子里,我们使用Batch模式,所以你能用以下代码来通知修改:
pRecordset->BatchUpdate (adAffectAll );
现在,你学会了怎样打开和查询数据,随后是操纵和更新数据。但是在ADO里还有更多的内容你必须来学习,这些内容中,最重要的是调用存储过程和参数化命令。
调用存储过程就象上面所说打开记录集一样容易。有一些细小的注意来创建输入参数和赋值,随后的代码会讨论这些。在ADO里传递参数有两个办法,第一个是通过Parameter对象,第二个是通过Refresh方法,但是我偏向于第一上方法,因为在性能上有优势。
这里,我介绍第一个办法。如下的代码行显示你怎样用Parameter对象设置输入参数和执行数据库里的Query1存储过程
_CommandPtr pCommand; _ParameterPtr pParam1, pParam2; CString str("Ma%"); pCommand.CreateInstance(__uuidof(Command)); pCommand->ActiveConnection = m_pConn; //Its the connection pointer //we opened formerly pCommand->CommandText = "Query1"; pCommand->CommandType = adCmdStoredProc; pParam1 = pCommand->CreateParameter ( _bstr_t ("DeptID"), adTinyInt, adParamInput, sizeof (BYTE), _variant_t ( long (11))); pCommand->Parameters->Append ( pParam1); pParam2 = pCommand->CreateParameter ( _bstr_t ("Name"), adVarChar, adParamInput, str.GetLength (), (_bstr_t) str); pCommand->Parameters->Append ( pParam2); _RecordsetPtr pRecordset; pRecordset.CreateInstance(__uuidof(Recordset)); pRecordset = pCommand->Execute(NULL, NULL, adCmdStoredProc);
代码开始,创建一个Command对象,接着设置ActiveConnection属性到一个已经打开的连接上,随后,在CommandText里指明你的存储过程的名称,对CommandType属性设置为adCmdStoredProc值。因为存储过程有两个输入参数,所以声明两个Parameter对象,接着通过调用CreateParameter方法来创建他们。CreateParameter的第一个参数指向一个可选的参数名称,第二个指明参数的类型,第三个决定参数是输入还是输出。笼四上参数代表变量的长度,第5个指明参数的值。
在指明了参数后,创建的参数分别被赋值给pParam1和Pparam2。创建的参数接着应该被添加(Appended)到Parameters集,为了达到此目的,我们调用了Append方法。现在,你的命令Command准备好执行了。
对于例子数据库里的Query1存储过程,你看一下就会理解,里面是一个简单的查询语句,它会返回一个rowset(记录集),所以在执行了command对象后,它可能返回一个存储在Recordset对象里的记录集。
对于上面引用的例子,为了代码的简洁性,我没有考虑错误处理。这里,我会讲述一个简单的错误处理,你可以代码里使用ADO的关键的任何地方来使用这个错误处理。通常情况下,因为ADO使用了COM,所以它抛出的是异常类型是_com_error,使用try-catch块来处理_com_error就可以实现异常处理:
try { HRESULT hr = m_pConn.CreateInstance(__uuidof(Connection)); if (FAILED( hr )) AfxMessageBox( "Can't create an intance of ADO.Connection" ); if (FAILED( m_pConn->Open( _bstr_t( "Provider=Microsoft.Jet.OLEDB.4.0;Data Source = ADOTestDB.MDB"), _bstr_t( "" ), _bstr_t( "" ),adModeUnknown ))) AfxMessageBox( "Can't open datasource" ); ... ... ... m_pConn->Close(); } catch( _com_error &e ) { _bstr_t bstrSource(e.Source()); _bstr_t bstrDescription(e.Description()); TRACE( "Exception thrown for classes generated by #import" ); TRACE( "\tCode = %08lx\n", e.Error()); TRACE( "\tCode meaning = %s\n", e.ErrorMessage()); TRACE( "\tSource = %s\n", (LPCTSTR) bstrSource); TRACE( "\tDescription = %s\n", (LPCTSTR) bstrDescription); } catch (...) { TRACE ( "*** Unhandled Exception ***" ); }
在上面代码里,Open函数调用不能发现在目录里的mdf文件,所以它产生一个决定了错误类型和编码的异常,另外还有一个关于错误的简洁描述。