㈠ vc++ ADO数据库
1. 生成应用程序框架并初始化OLE/COM库环境
创建一个标准的MFC AppWizard(exe)应用程序,然后在使用ADO数据库的InitInstance函数中初始化OLE/COM库(因为ADO库是一个COM DLL库)。
本例为:
BOOL CAdotestDlg::OnInitDialog()
{
::CoInitialize(NULL); //初始化OLE/COM库环境
}
程序最后要调用 ::CoUninitialize();//释放程序占用的COM 资源。
另外:
m_pRecordset->Close(); 注意!!!不要多次关闭!!!!!!!!!!!!
m_pConnection->Close();
m_pRecordset = NULL;
m_pConnection = NULL;
2. 引入ADO库文件
使用ADO前必须在工程的stdafx.h文件最后用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下:
#import "C:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
ADO类的定义是作为一种资源存储在ADO DLL(msado15.dll)中,在其内部称为类型库。类型库描述了自治接口,以及C++使用的COM vtable接口。当使用#import指令时,在运行时Visual C++需要从ADO DLL中读取这个类型库,并以此创建一组C++头文件。这些头文件具有.tli 和.tlh扩展名,读者可以在项目的目录下找到这两个文件。在C++程序代码中调用的ADO类要在这些文件中定义。
程序的第三行指示ADO对象不使用名称空间。在有些应用程序中,由于应用程序中的对象与ADO中的对象之间可能会出现命名冲突,所以有必要使用名称空间。如果要使用名称空间,则可把第三行程序修改为: rename_namespace("AdoNS")。第四行代码将ADO中的EOF(文件结束)更名为adoEOF,以避免与定义了自己的EOF的其他库冲突。
3.利用智能指针进行数据库操作
在CaboutDlg头文件中定义两个ADO智能指针类实例,并在对话框中加入一个ListCtrl。
class CAdotestDlg : public CDialog
{
_ConnectionPtr m_pConnection;
_RecordsetPtr m_pRecordset;
ClistCtrl m_List;
......
}
ADO库包含三个智能指针:_ConnectionPtr、_CommandPtr和_RecordsetPtr。
_ConnectionPtr通常被用来创建一个数据连接或执行一条不返回任何结果的sql语句,如一个存储过程。
_CommandPtr返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定、游标控制等。
在使用ADO程序的事件响应中OnButton1加入以下代码:
void CAdotestDlg::OnButton1()
{
m_List.ResetContent();
m_pConnection.CreateInstance(_uuidof(Connection)); //初始化Connection指针
m_pRecordset.CreateInstance(_uuidof(Recordset));//初始化Recordset指针
try
{
m_pConnection->Open("DSN=ADOTest","","",0); //连接叫作ADOTest的ODBC数据源
//注意:这是连接不需要用户ID或密码的open 函数
// 否则形式为 ->Open("DSN=test;uid=sa;pwd=123;","","",0);
// 执行SQL语句得到一个记录集把其指针赋值给m_pRecordset
CString strSql="select * from middle";
BSTR bstrSQL = strSql.AllocSysString();
m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,adCmdText);
//adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句
while(!m_pRecordset->adoEOF)//遍历所有记录
{
//取纪录字段值方式之一
_variant_t TheValue; //VARIANT数据类型
TheValue = m_pRecordset->GetCollect("BIG_NAME");//得到字段BIG_NAME的值
if(TheValue.vt!=VT_NULL)
m_List.AddString((char*)_bstr_t(TheValue));
//将该值加入到列表控件中
//取纪录字段值方式之二
// _bstr_t TheValue1=m_pRecordset->Fields->GetItem("BIG_NAME")->Value;
// CString temp=TheValue1.();
// m_List.AddString(temp);
//数据类型转换
_variant_t vUsername,vBirthday,vID,vOld;
TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",
vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday);
m_pRecordset->MoveNext();//转到下一条纪录
}
m_pRecordset->Close();
m_pConnection->Close();
}
catch (_com_error e)//异常处理
{
AfxMessageBox(e.ErrorMessage());
}
m_pRecordset->Close(); //注意!!!不要多次关闭!!!!否则会出错
m_pConnection->Close();
m_pRecordset = NULL;
m_pConnection = NULL;
}
程序中通过_variant_t和_bstr_t转换COM对象和C++类型的数据, _variant_t类封装了OLE自治VARIANT数据类型。在C++中使用_variant_t类要比直接使用VARIANT数据类型容易得多。
好,编译后该程序就能运行了,但记住运行前要创建一个叫ADOTest的ODBC数据源。该程序将把表middle中的BIG_NAME字段值显示在列表控件中。
4.执行SQL命令并取得结果记录集
为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;
并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset");
SQL命令的执行可以采用多种形式,下面我们一进行阐述。
(1)利用Connection对象的Execute方法执行SQL命令
Execute方法的原型如下所示:
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options )
其中CommandText是命令字串,通常是SQL命令。
参数RecordsAffected是操作完成后所影响的行数,
参数Options表示CommandText中内容的类型,Options可以取如下值之一:
adCmdText:表明CommandText是文本命令
adCmdTable:表明CommandText是一个表名
adCmdProc:表明CommandText是一个存储过程
adCmdUnknown:未知
Execute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。
_variant_t RecordsAffected;
///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,整形old,日期型birthday
m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",
&RecordsAffected,
adCmdText);
///往表格里面添加记录
m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) VALUES (1, ''''Washington'''',25,''''1970/1/1'''')",&RecordsAffected,adCmdText);
///将所有记录old字段的值加一
m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText);
///执行SQL统计命令得到包含记录条数的记录集
m_pRecordset = m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText);
_variant_t vIndex = (long)0;
_variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量
上两句可以写成— _variant_t vCount = m_pRecordset->GetCollect((_variant_t)((long)0));
m_pRecordset->Close();///关闭记录集
CString message;
message.Format("共有%d条记录",vCount.lVal);
AfxMessageBox(message);///显示当前记录条数
(2)利用Command对象来执行SQL命令
_CommandPtr m_pCommand;
m_pCommand.CreateInstance("ADODB.Command");
_variant_t vNULL;
vNULL.vt = VT_ERROR;
vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数
m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它
m_pCommand->CommandText = "SELECT * FROM users";///命令字串
m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集
在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。
(3)直接用Recordset对象进行查询取得记录集
实例——
void CGmsaDlg::OnDBSelect()
{
// TODO: Add your control notification handler code here
_RecordsetPtr Rs1; //定义Recordset对象
_bstr_t Connect("DSN=GMS;UID=sa;PWD=;");//定义连接字符串
_bstr_t Source ("SELECT count(*) FROM buaa.mdb010"); //要执行的SQL语句
::CoInitialize(NULL); //初始化Rs1对象
HRESUL hr = Rs1.CreateInstance( __uuidof( Recordset ) );
//省略对返回值hr的判断
Rs1->Open( Source,
Connect,
adOpenForwardOnly,
adLockReadOnly,
-1 );
_variant_t temp=Rs1->GetCollect(_variant_t((long)0));
CString strTemp=(char* )(_bstr_t)temp;
MessageBox("OK!"+strTemp);
}
例如
m_pRecordset->Open("SELECT * FROM users",
_variant_t((IDispatch *)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
adCmdText);
Open方法的原型是这样的:
HRESULT Recordset15::Open ( const _variant_t & Source,
const _variant_t & ActiveConnection,
enum CursorTypeEnum CursorType,
enum LockTypeEnum LockType,
long Options )
其中:
①Source是数据查询字符串
②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象)
③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
enum CursorTypeEnum
{
adOpenUnspecified = -1,///不作特别指定
adOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用
adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。
adOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。
adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。
};
④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:
enum LockTypeEnum
{
adLockUnspecified = -1,///未指定
adLockReadOnly = 1,///只读记录集
adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制
adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作
adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。
};
⑤Options可以取如下值之一:
adCmdText:表明CommandText是文本命令
adCmdTable:表明CommandText是一个表名
adCmdProc:表明CommandText是一个存储过程
adCmdUnknown:未知
5. 记录集的遍历、更新
根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday
以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,
更改其年龄,保存到数据库。
_variant_t vUsername,vBirthday,vID,vOld;
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance("ADODB.Recordset");
m_pRecordset->Open("SELECT * FROM users",
_variant_t((IDispatch*)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
adCmdText);
while(!m_pRecordset->adoEOF)
{
vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数,
///你也可以直接给出列的名称,如下一行
vUsername = m_pRecordset->GetCollect("username");///取得username字段的值
vOld = m_pRecordset->GetCollect("old");
vBirthday = m_pRecordset->GetCollect("birthday");
///在DEBUG方式下的OUTPUT窗口输出记录集中的记录
if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != VT_NULL)
TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",
vID.lVal,
(LPCTSTR)(_bstr_t)vUsername,
vOld.lVal,
(LPCTSTR)(_bstr_t)vBirthday);
m_pRecordset->MoveNext();///移到下一条记录
}
m_pRecordset->MoveFirst();///移到首条记录
m_pRecordset->Delete(adAffectCurrent);///删除当前记录
///添加三条新记录并赋值
for(int i=0;i<3;i++)
{
m_pRecordset->AddNew();///添加新记录
m_pRecordset->PutCollect("ID",_variant_t((long)(i+10)));
m_pRecordset->PutCollect("username",_variant_t("叶利钦"));
m_pRecordset->PutCollect("old",_variant_t((long)71));
m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15"));
}
m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动到第二条记录处
m_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄
m_pRecordset->Update();///保存到库中
备注:多次查询可把查询过程做成一个函数ExecuteSQL让m_pRecordset获得连接指针m_pConnection查询结果
void ExecuteSQL(_ConnectionPtr m_pConnection, _RecordsetPtr m_pRecordset,CString strSql)
{
//执行Select 语句
BSTR bstrSQL = strSql.AllocSysString();
try
{
m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,adCmdText);
//adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句
}
catch(_com_error error)
{
CString errorMessage;
errorMessage.Format("%s",(LPTSTR)error.Description());
AfxMessageBox(errorMessage);
}
}
//出错处理:
3127——没有找到目标表
3092——目标表已经存在
例如:
catch(const _com_error e)
{
AfxMessageBox(e.Description());
long errorCode=e.WCode();
if(3127==errorCode) AfxMessageBox("表不存在");
if(3092==errorCode) AfxMessageBox("表已经存在");
return FALSE;
}
㈡ 连接数据库需要ADO组件中的什么对象,创建它的方法是什么/
一、概述
“性能”这一术语有着几种不同的、差异微妙的含义。当人们谈到某个东西性能多少好时,他们想要表达的意思可能就是在一定的时间之内它完成了多少工作。例如,一个性能好的发动机运行起来更稳定,产生的动力更强大。对于开发小组,你同样也可能应用这个判断标准:一个性能好的开发小组工作时比较安静,而且能够生产出大量高质量的代码。对我来说,性能至少意味着两件事情——我的代码运行起来有多好,我的开发小组和我本人工作效率怎么样。无论哪一方面,本文介绍的技巧都将起到一定的帮助作用:帮助你更快地编写代码,帮助你编写更快的代码——安静地完成这一切,减少这样那样的错误。本文介绍的技巧主要面向ADO,特别是如何通过ADO访问SQL Server。但与此同时,我还将涉及一些适用范围更广的COM技巧,它们适用于你所编写的所有Visual Basic代码。
为了了解从哪些SQL Server数据访问代码编写技术、哪些体系、哪些开发习惯可以得到最好的性能,我已经花了不少时间。一些情况下,对于应用的整体性能来说,单一的技术意义很小,除非我们通过循环将性能的改善程度成倍放大。例如,在一个客户机/服务器应用中,当我们不是通过指定ODBC数据源(DSN)的方式连接数据库时,大约能够节省一到二秒的时间。对于应用整体的适用性或性能来说,这部分节省的时间所产生的影响很小。但是,如果我们在一个中间层组件上应用这种技术,这个组件每分钟(或每小时,每天)都要建立和关闭数据库连接数百(甚至数千)次,那么,这种技术将显着地影响系统的性能表现。因此,对于我在这里讨论的每一种技术,请务必考虑这个倍数因子——即,在一定的时间周期内,你的系统将执行同一段代码多少次。
当你开始寻求改进性能的方案时,请考虑一下你的应用(组件,或者是ASP代码)大部份的等待和处理时间花在什么地方。如果你发现应用程序把大量的时间花在等待Open或Execute方法执行完成,那么,你应该认真地检查一下服务器端的查询策略。包括ADO在内,所有的数据访问接口等待查询结果的时间都相同。例如,如果你有一个查询,SQL Server需要20秒才能完成它,不论用来执行该查询的是什么接口,没有一种接口能够比其他接口以更快的速度返回结果。虽然有些接口打开连接的速度比较快,有些接口处理结果集的速度比较快,但没有一种接口能够影响数据库引擎编译和执行查询的速度。因此,如果你的查询具有太高的“挑战性”——例如你没有对索引进行优化,你没有使用存储过程,服务器负载过重,或者你要求返回的记录数量太多——那么,世界上没有一种ADO技术能够帮助你提高性能。除非你解决了这些基本的查询问题,否则没有一种性能调整技术能够显着地改善整体性能。SQL Server的Query Analyzer是一个分析查询性能的优秀工具。它能够用图形的方式显示查询的执行过程,并对改进性能的方法提出建议。
如果你能够确信查询具有较高的效率,那么,你可以使用本文介绍的技术进一步调整ADO代码的性能。这里介绍的技巧将从各个方面帮助你简化和改进ADO编程,包括:建立和维护连接,构造和提交执行速度更快的查询,提高处理查询结果的效率,等等。
二、建立连接
在一个客户机/服务器应用中,我们可以用好几种方法把建立和初始化数据库连接所需要的时间隐藏起来,使得应用程序既能够打开连接,又不需要用户等待应用程序启动。首先,我们可以尝试异步连接。使用异步连接时,ADO启动连接操作之后,不等待连接完成就把控制权返回给应用程序——这样,应用程序就能够接着执行大部份初始化操作,以更快的速度完成form_load事件处理。如果关闭并重新建立连接的时间小于连接池释放连接的时间,那么这个连接实际上是即时的。但在许多情况下(特别是用户数量不多时),让连接保持打开状态更具有现实意义。在中间层组件或ASP页面内部,如果数据库查询多次重复出现,我建议你让Connection对象保持打开状态。
另外一个改进连接性能的办法是,避免使用带有DSN的ODBC。在Microsoft,ODBC已经转入了Quick Fix Engineering(QFE,快速修理工程)状态,它意味着:除非发现重大BUG,该公司将不再在ODBC或它的驱动程序上花时间。另外,考虑性能和部署问题时,ODBC DSN也是一个必须关注的问题。DSN必须安装到客户系统上,要求进行注册表查找,与OLE DB连接相比,它建立连接所需要的时间更长——特别是当你用直接编码的方式指定ConnectionString时,这一点尤其突出。从实际效果来看,避免使用DSN降低的系统开销很有限:如果完全取消连接建立过程,对于每个连接,你也许能够剩下二到五秒时间(假设数据库连接池中已经没有连接)。然而,如果你的应用程序需要频繁地建立连接,节省的时间累计起来就很可观了。
建立数据库连接的时候,你要选择一个数据提供者。Microsoft建议我们使用OLE DB提供者替代默认的ODBC提供者。对比最新的OLE DB本地提供者和功能类似但较早的ODBC提供者,我感到前者令人不愉快的意外之事较少。但无论是哪种情况,你都应该在决定使用某个新的提供者之前对应用进行完整地测试——代码的性能、支持的功能、行为方式都有可能发生变化。
在中间层和ASP中,在保持连接打开的情况下,我们不能(从实践来看)创建出可伸缩的组件——至少在多次调用之间是这样的。一般地,当IIS引用和释放组件、ASP页面的实例时,组件和ASP页面被频繁地装入、丢弃。由于基于ADO的代码每次执行时都必须建立、使用、释放数据库连接,最小化连接复杂程度的策略对性能的提高程度达到了可明显测量的程度。在这些情形下,对于我们连接数据库的速度来说,连接/会话池有着重要的意义。如果你为Command对象的ConnectionString属性指定合适的值(即,每次使用同样的服务器、初始目录、登录ID和其他参数),那么,连接已经打开且处于可用状态的机会很大。如果连接池中能够找到匹配的连接,连接(或重新连接)的时间将接近0(通常小于250 ms)。
然而,如果ADO(或VB)代码不释放Connection对象,或者,我们在不同的实例之间改换了ConnectionString,OLE DB必须每次建立一个新的连接。如果出现了这种情况,我们将很快耗尽连接池内可用连接的数量。要确保连接被释放,我们必须在关闭连接之后把Connection对象设置为Nothing。另外,不要在Recordset Open方法中使用ConnectionString,而是以独立的方式打开Connection对象;这样,当我们要关闭Connection对象以及要把它设置成Nothing的时候,引用它就很方便了。
三、构造和提交查询
在构造查询的时候,要搞清楚为什么必须这么做、为什么不能那么做是一个很复杂的问题。然而,一些基本的指导方针能够让构造高效查询的过程更加流畅、轻松。一般地,你不应该让查询浪费服务器时间。下面几个技巧能够帮助你构造出更好、更高效的查询。
不要强制SQL Server每次执行查询的时候重新编译和构造查询执行计划。避免这种重复操作的一种简单方法是使用带有参数的存储过程。注意尽量不要使用ADO Command对象的Prepare属性——有时它不能正确工作。如果使用存储过程,你还可以通过消除不必要的“受影响行数”返回值进一步提高ADO性能——只需在存储过程中加入SET NOCOUNT ON就可以了。
尽量减少与服务器的通信次数。如果你有几个相关的操作要执行,请把它们合并为一个存储过程,或者是一个可以在服务器上作为脚本执行的复合查询。避免使用方法(比如Refresh)和不适当的Parameters集合引用,它们会强制ADO增加额外的服务器通信过程。
在客户机/服务器应用中,只构造Command对象一次,而不是每次使用Command对象的时候重新构造。你可以重新设置Command的参数值,然后在需要时执行它。
当查询返回的不是一个记录集时,确保使用了adExecuteNoRecords选项,告诉ADO越过所有那些用来接收和构造记录集(Recordset格式)的代码。你可以把adExecuteNoRecords选项传递给Execute方法,或把它作为Command的选项。
执行返回简单记录集的存储过程时,不要使用Command对象。所有的存储过程(以及Command对象)可以作为Connection对象的COM方法出现。让存储过程作为Connection对象的方法出现有着显着的性能优势,同时它也简化了代码。尽管这种技术对于那些有Return Status值或Output参数的存储过程没有什么帮助,但对于动作查询(INSERT、DELETE等)以及那些返回一个或多个记录的查询来说,这种技术很有用。把存储过程作为Connection的方法之后,你可以用方法参数的形式传入存储过程的输入参数;如果调用存储过程返回了一个记录集,你可以通过方法调用中最后一个参数引用该Recordset。例如,下面的ADO语句执行一个名为“Fred”的存储过程,Fred存储过程有两个输入参数,返回一个Recordset:
MyConnection.Fred "InputArg1", 2, myRecordset
编写代码的时候,不要寄希望于VB的自动完成功能会把存储过程或Command对象名字视为合法的Connection对象的方法。在正式运行之前,COM不会解析这类名字。
除非绝对必要,否则不要返回记录集。当正在执行的查询返回记录时,ADO就会构造一个Recordset对象。构造Recordset对象的开销很大,因此你应该尽量避免使用Recordset对象。注意有时候执行查询虽然返回结果,但不是返回记录。例如,你可以通过Return Status参数返回整数值。另外,你可以返回Output参数来替代需要构造Recordset对象的记录集,SQL Server允许返回的Output参数多达1000个。
只要有可能,请用动作查询(INSERT,UPDATE,DELETE和执行这些操作的存储过程)替代可更新的Recordset游标。此时,你应该使用Execute方法和它的adExecuteNoRecords选项,确保ADO能够知道查询不需要构造Recordset对象。
除非必要,否则不要请求服务器进行排序。大多数情况下,对于一个适度大小的Recordset对象,当它被发送到客户端之后,排序速度将更快。另外,如果让ADO客户程序排序Recordset中的记录,则客户应用程序能够按照用户选择的次序排序,从而提高了灵活性。
在编写查询之前了解索引的结构。创建合适的索引,调整查询的语法以利用这些索引,你将能够提高记录提取的速度。Query Analyzer能够帮助你决定是否有必要添加更多的索引。
不要一次性返回太多的记录。很多时候,容量太大的记录集会严重地影响应用程序的性能。只返回那些当前你需要的记录,如果客户程序需要更多的记录,则以后随时提取。通过带有参数的WHERE子句,或者灵活地运用TOP N查询,限制查询的范围。
不要返回太多的列。避免使用SELECT *。SELECT *语句告诉SQL Server返回所有的列,不管实际存在的列有多少。只选择那些你需要的列,这样,当有人为表增加了更多的列时,你不会得到大得出奇的结果集。
避免使用游标。如果你必须使用游标,那么不要使用那些所需资源数量超过必要的游标类型。如果没有必要,不要要求游标提供滚动、更新和数据缓冲能力。
详细地告诉ADO你想要它做些什么。打开Recordset或者构造Command对象时,不要忘了设置CommandType选项。它避免了ADO“猜测”你的意图,你将能够减少与服务器的通信,而且使得代码更加稳定。
另外,学习使用诊断工具,测定运行在服务器上的代码和应用程序的代码占用了多少时间——以及这些时间花在哪里。在这方面,SQL Server Profiler是一个宝贵的工具。它能够阐明你的代码在要求服务器做些什么,能够在草率构造的查询中或对于错误选择的命令属性突出显示。另外,Query Analyzer还能够用图示的方式显示出SQL Server将如何执行查询,提出改进查询的建议,帮助你调整查询。Query Analyzer甚至还能够执行它提出的建议(例如,添加或者删除索引),你只需点击一下按钮就可以完成。
四、处理查询结果
查询结果记录发送到客户端之后,客户端应用程序可能需要相当可观的时间去处理结果集。每一种体系(客户机/服务器,多层体系中的中间层,以及ASP)都为优化这个阶段的代码提供了相应的技术。下面是几个能够显着改善性能的技巧。
我在代码中看到最多的错误之一是:在引用Recordset Field.Value的时候,使用延迟绑定(Late Binding)。由于代码需要频繁地引用Value属性,而且通常要引用的Field对象有很多,本文前面提到的倍数因子将起到重要影响——因此,所有这里介绍的技巧能够显着地改善性能。一些开发者使用延迟绑定技术的原因在于,他们想要明确地标识出SELECT语句选择了哪些行。为了这个目标,许多人使用了用引号包围字符串的做法。例如,为了引用记录集RS字段集合中的“Cows”字段,你可能使用:
RS("Cows")或者:RS.Fields("Cows").Value
后面这种方法显式地引用了记录集内Fields集合中指定成员的Value属性。这种方法要稍微快一点,而且当你把这些代码向Visual Basic.NET迁移时,它的向上兼容性也要好一些。上述方法的一种变化是使用感叹号(!)操作符:
RS!Cows
与先行绑定(Early-Binding)相比,采用上述方法时COM进行解析的时间要长得多,这是因为它们强制COM在运行时(而不是在编译时)解析对Value属性的引用,每一次对该对象的引用都要求有一系列类似的、后台进行的查找过程。
然而,使用延迟绑定时,不存在代码引用的是哪一个列这类问题。如果你完全按照下列方式编写代码:
RS(0) ' 指向第一个列(Fields集合的成员)
这时,COM能够在编译时解析Value属性地址,代码的运行速度将加快。但是,只有那些了解查询所返回的列以及返回次序的人能够理解这行代码到底指向了哪一个列。如果开发者不具备控制查询数据源的权限(这是很常见的情况),这种方法可能带来问题。为了确定RS(0)引用了哪一个SELECT列,你必须找出生成该Recordset的是哪一个SELECT语句、搞清楚SELECT语句所返回的各个列。
然而,有几种技术允许你既能够实现快速地运行时引用,同时保证代码的可读性。其中一种方法的要求如下:开发者必须创建一个查询所返回列的枚举列表。如果查询被改变,比如返回更多的列,或者列的次序发生变化,开发者必须修改和重新部署枚举列表。遗憾的是,要保持枚举列表与查询的匹配,对于管理者来说是一个有些困难的任务。例如,为了快速、明白地标识出ADO代码引用的是哪一个列,你可以结合下面的SELECT语句和枚举列表:
SELECT CatName, CatType, CatSize from Cats Where...Enum enuCatsQuery CatName CatType CatSizeEnd Enum
注意,SELECT语句返回的列与枚举列表中声明的列完全匹配。此后,当你需要引用Recordset的Fields集合时,可以使用下面的代码:
StrMyName = Rs(enuCatsQuery.CatName)
按照这种方法,代码不仅具有较好的可读性,而且它仍旧是编译时绑定,代码的运行速度明显加快。
然而,要避免延迟绑定,你还可以使用另外一种方法。[email protected]列表服务上一场长时间的讨论得出了一种我称之为预先绑定(Prebinding)的方法,它结合了两种技术。当你只需引用Field对象一次时,这种技术没有什么帮助;但在客户机/服务器应用中,预先绑定方法非常理想。使用这种方法时,你要创建多个独立的、命名的Field对象,并把这些对象设置为Recordset对象Fields集合中的成员。编写代码的时候,你首先要为每一个想要使用的字段创建一个命名的Field对象,例如:
Dim fldName as ADODB.FieldDim fldType as ADODB.FieldDim fldSize as ADODB.Field
创建这些Field对象需要一定的开销。然而,你应该估量一下,这是一次性的开销,但它却能够戏剧性地改善性能。
打开Recordset之后,你只需一次性地把这些命名的Field对象设置为SELECT查询选择出来的列:
If fldName is Nothing thenSet fldName = RS!CatNameSet fldType = RS!CatTypeSet fldSize = RS!CatSizeEnd if
你可以在这里用引号包围字符串的方法引用列,甚至也可以使用感叹号操作符。由于这里的代码只运行一次,不论使用什么方法,性能的差异都不大。接下来,当你需要引用Field对象(查询之后)时,只需使用预先绑定的变量即可:
strName = fldNamestrType = fldTypestrSize = fldSize
这种预先绑定方法的性能甚至比序数引用方式(例如RS(0))都要好。
五、客户机/服务器、中间层和ASP策略
在编写代码的时候,你还必须考虑到其他一些影响性能的因素。其中一些因素与ADO没有什么关系——它们与COM有关。Microsoft最近的一份白皮书指出,在Windows 2000 ASP页面中执行ADO操作(连接,查询,处理)要比调用COM组件执行同样的代码更快。这个结论并不令人奇怪:当我们从VB调用一个外部COM组件(处于当前进程之外的一些代码),访问COM组件以及把控制传递给它时在后台进行的操作复杂得出奇,而且速度很慢。虽然我们没有必要刻意避免调用COM组件去运行ADO代码。但是,我们不应该简单地把多个独立的ADO操作封装成大量的小型COM组件,然后在需要的时候每次都去调用它们。相反,我们应该尽量把全部逻辑封装到一个COM组件里面,使得程序只需一次调用,COM就能够完成大多数(如果不是全部)操作。我相信,你已经发现运行二进制形式的(例如COM组件)ADO代码要比运行ASP之类的解释执行代码要快。因此,你应该寻找一些方法,减少进入COM组件和从COM组件返回所需要的昂贵开销。
如果你离不开Command对象,或者不能预先绑定Field对象并在必要时重用,那么你应该考虑避免多余的对象创建操作的技术。在这种情况下,把存储过程作为Connection对象的方法调用有着更重要的意义。另外,用先行绑定的方式引用Field属性也有助于改善性能。记住操作完成后必须进行的清理工作:关闭Connection和Recordset对象,然后把它们设置成Nothing。
为了让代码和代码编写者都表现出最好的性能,请记住以下基本规则:利用连接池和异步连接;减少ADO代码和数据库服务器通信的次数;选用一种COM-先行绑定技术;除非必要,尽量避免使用代价昂贵的ADO对象,例如Recordset和Command对象;如有可能,用Return Status和Output参数替代记录集。尽可能地提高查询的效率,如有可能,不要忘了利用存储过程。详细地告诉ADO你想要它做些什么,避免让ADO猜测你的意图——显式地指定ADO CommandType,使用adExecuteNoRecords之类的选项。
对于本文所介绍的所有技巧和其他文章提出的编程忠告,我建议你以审视的目光看待它们。我们所做的工作、所编写的代码、所构造的系统,都属于非常复杂的东西,许多不断变化的因素影响着它们。理解了这一点之后,如果你对本文所讨论的某一项技术感兴趣,可以先进行一下试验。如果它确实有效,那么就正式实现它,然后再进行测试。如果这时它仍旧有效,那么恭喜你。如果它不再有效,你得看看是否违反了应用该技术的必要条件。
㈢ 使用VB+ADO设计与开发一个简单的数据库应用系统(数据库采用 SQL Server),编个小程序。
数据库的
要求是什么。
任务是
㈣ 使用VB+ADO设计与开发一个简单(综合)的数据库应用系统(数据库采用 SQL Server)
到codefans上vb 数据库中
㈤ C++ :ADO实现数据访问层的封装 实现对数据库的修改时 。。。
允许开发人员编写访问数据的代码而不用关心数据库是如何实现的,而只用关心到ADO向我们提供了一个熟悉的,高层的对OLE DB的Automation封装接口。对那些
㈥ ado封装描述
一、前言
用过ADO的人都知道, 调用ADO要处理很多"麻烦"的事情,如异常处理等,要写很多try - catch块. 有点不甚其烦。我干脆把常用的函数都封装起来,免去老是要写try - catch块的麻烦。做起来虽然没有什么技术含量,但也比较烦琐,所以只完成了一部分,且由于时间及个人水平有限,没有对封装的东西作全面测试,并必定有很多错误,但想到对某些朋友可能有用。所以先"捐"出来了。^-^.
在介绍这两个类之前,让我们先来了解一下ADO,本文假设你已有一定的编程能力:
二、了解ADO的结构体系
ADO(ActiveX Data Object, Active 数据对象)是Microsoft提供的一种面向对象,与语言无关的数据访问应用编程接口。据大部分资料介绍,它有如下主要特点:
一:易于使用。
二:可以访问多种数据源。
三:访问速度快,效率高:
四:方便Web应用。
五:技术编程接口丰富。
六:低内存支出和占用磁盘空间较少.
正是看到ADO这么多优点,使我对用ADO开发数据库产生了兴趣.ADO用起来也如前面所说的一样,确实不难。总的来说,ADO模型包括了下列对象:连接(Connection)、命令 (Command)、记录集 (Recordset)、字段 (Field)、参数 (Parameter)、错误 (Error)、属性 (Property)、集合 、事件.它们之间的关系如下图:
(1)我们最常用的主要是Connection、Recordset及Command这三个对象.
(2)对于访问一个数据库来说,我们一般先建立一个ADO连接.
(3)ADO连接可以直接执行SQL语句来操纵数据库,但如果我们要对数据在应用程序和数据源之间进行存取的话,就需要用到记录集对象。一个ADO连接可以有多个ADO连接,但一个ADO连接一般只能对应一个且必须对应一个ADO连接.
(4)另外如果你可进行更高级别的访问的话,还可能要用到命令对象。例如要调用存储过程等。
(5)一个记录集包含有一个字段集,一个字段集则包含有多个字段对象。
(6)同样一个命令对象也包含一个参数集,一个参数集则包含有多个参数对象。
(7)连接对象也有一个错误集并包含有多个错误对象。
这就是ADO各对象之间大致的关系.
三、了解ADO连接
在使用数据库之前,要先建立连接.一般先用CreateInstance方法创建ADO连接对象,然后就可以用Open方法连接到数据库。它的原型是 Open(BSTR ConnectionString, BSTR UserID, BSTR Password, long Options);其中UserID和Password如果在ConnectionString已经指明了用户名和密码,一般就可以不必管它们.Options指的是是以同步方式(adConnectUnspecified)还是以异步方式(adAsyncConnect)进行连接,默认为同步.这个函数的关键这处在于ConnectionString参数,它决定了我们将以什么方式连接到什么数据源,例如:
如果是Access数据库,它的格式则一般为: "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=db.mdb";
如果是SQL Server: "Provider=SQLOLEDB.1;Data Source=sqlservername;Initial Catalog=master;UserID=sa; PWD=password";
具体的内容视你的环境而定.要连接到其他数据库,请参考相关的资料.
例如:
_ConnectionPtr pConnection;
LPCSTR strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb";
//创建 Connection 对象---------------------------
HRESULT hr = pConnection.CreateInstance("ADODB.Connection");
//设置连接时间-----------------------------------
pConnection->put_ConnectionTimeout(long(5));
if (SUCCEEDED(hr))
{
// 连接数据库---------------------------------------------
pConnection->Open(strConnect, "", "", adConnectUnspecified))
}
四、了解ADO记录集
创建了ADO连接,我们就可以通过ADO记录集来访问数据库了.同样,在使用记录集之前要先创建对象.然后调用Open方法打开记录集.它不但可以执行普通的SQL语句,还可以调用存储过程等等:
Open(VARIANT Source, VARIANT ActiveConnection, CursorTypeEnum CursorType,LockTypeEnum LockType, LONG Options).
其中ActiveConnection参数为一个有效的 Connection 对象名,就是我们在上面所说过的ADO连接对象.
CursorType参数指的是记录集光标类型,在官方的资料中是这样说明它的取值类型的:
1.adOpenForwardOnly 仅向前游标,默认值。除了只能在记录中向前滚动外,与静态游标相同。当只需要在记录集中单向移动时,使用它可提高性能。
2.adOpenKeyset 键集游标。尽管从您的记录集不能访问其他用户删除的记录,但除无法查看其他用户添加的记录外,键集游标与动态游标相似。仍然可以看见其他用户更改的数据。
3.adOpenDynamic 动态游标。可以看见其他用户所作的添加、更改和删除。允许在记录集中进行所有类型的移动,但不包括提供者不支持的书签操作。
4.adOpenStatic 静态游标。可以用来查找数据或生成报告的记录集合的静态副本。另外,对其他用户所作的添加、更改或删除不可见。
我们现在可不用管这么多,就用默认的adOpenStatic类型吧.
LockType参数,用于指示在什么时候锁定记录:
AdLockReadOnly (默认值)只读 - 不能改变数据。
AdLockPessimistic 保守式锁定(逐个) - 提供者完成确保成功编辑记录所需的工作,通常通过在编辑时立即锁定数据源的记录。
AdLockOptimistic 开放式锁定(逐个) - 提供者使用开放式锁定,只在调用Update 方法时才锁定记录。
AdLockBatchOptimistic 开放式批更新-用于批更新模式(与立即更新模式相对)。
Options参数指的是操作类型:
adCmdText 指示strSQL为命令文本, 即普通的SQL语句.
adCmdTable 指示ADO生成SQL查询以便从在strSQL中命名的表中返回所有行.
adCmdTableDirect 指示所作的更改在strSQL中命名的表中返回所有行.
adCmdStoredProc 指示strSQL为存储过程.
adCmdUnknown 指示strSQL参数中的命令类型为未知
adCmdFile 指示应从在strSQL中命名的文件中恢复保留(保存的)Recordset.
adAsyncExecute 指示应异步执行strSQL.
adAsyncFetch 指示在提取 Initial Fetch Size 属性中指定的初始数量后,应该异步提取所有剩余的行.如果所需的行尚未提取,主要的线程将被堵塞直到行重新可用.
adAsyncFetchNonBlocking 指示主要线程在提取期间从未堵塞. 如果所请求的行尚未提取,当前行自动移到文件末尾.
唉又是一大串,如果你只是要执行SQL语句,就把它设为adCmdText吧. 这样Source就是你要执行的SQL语句了.
例如:
LPCSTR strSQL = "select * from vckbasetable";
_RecordsetPtr pRecordset;
pRecordset.CreateInstance("ADODB.Recordset");
pRecordset->Open(_bstr_t(strSQL),
_variant_t((IDispatch*)pConnection, true),
adOpenStatic,
AdLockOptimistic ,
adCmdText);
ADO中读取记录集中指定字段的值一般有两种方法:
第一种:
FieldsPtr pFields;
pRecordset->get_Fields(&pFields);
pFields->Item[L"COLUMN_NAME"]->Value;
//或pFields->Item[long(index)]->Value;
//其中index为整型或长整型.GetFields()函数返回的是记录集对象的字段集合对象的指针.
第二种:
pRecordset->get_Collect("COLUMN_NAME");
//或pRecordset->get_Collect(long(index));
它们都将返回一个_variant_t类型的值,推荐使用后一种方法.
例如:
int ncol = rset.GetFieldsCount();
while (!rset.IsEOF())
{
for (int i = 0; i < ncol; i++)
{
rset.GetValueString(value, (long)(i));
}
rset.MoveNext();
}
四、了解ADO字段.
一个记录集通常包含多个字段,通过访问记录,我们可以得到很多有用的信息,如字段名,字段的数据类型,定义的宽度,实际占有的宽度等等:
一般用记录集的get_Fields方法取得字段集合对象:
FieldsPtr pFields;
Recordset->get_Fields(&pFields);
然后可以获得相应的字段对象:
long lIndex = 0;
FieldPtr pf = pFields->GetItem(_variant_t(lIndex));
//或: FieldPtr pf = pFields->GetItem("COLUMN_NAME");
字段对象有很多有用的属性,这些可以参考我的源代码或其他相关资料.如:
get_ActualSize(long *pl) //实际宽度
get_Attributes(long *pl) //属性
get_DefinedSize(long *pl) //定义宽度(以字节为单位,如整型为4,长整型为8...)
get_Name(BSTR *pbstr) //字段名
get_Type(DataTypeEnum *pDataType) //数据类型
get_Value(VARIANT *pvar) // 字段的值
有了以上对ADO的基本了解后,我们将正式开始编写应用程序,请看下文。
使用ADO封装类的数据库程序开发实例(下)
type=text/javascript>
最新评论 [发表评论] [文章投稿] 查看所有评论 推荐给好友 打印
高手啊,问个问题,比较急!
怎么能够同步查询数据库中的数据呢?
麻烦点也没什么! ( sunshinesky 发表于 2005-5-31 18:21:00)
有些类我还是不清楚在本例子中有什么作用,能讲清楚点吗?谢谢! ( keystone 发表于 2004-8-6 11:42:00)
伟大 ( kl334 发表于 2004-7-31 18:24:00)
代码已经更新,修复了几个严重的bugs. ( vckbase 发表于 2003-4-9 0:40:00)
不错,方便了不少。例子中
CAccessView::UpdateGrid()对宽度的计算稍有不妥。
如下
int nwidth = rset.GetFieldDefineSize(i) * 200;
nwidth = nwidth > 2009 ? 2009 : nwidth;
当是memo字段时结果大于int型最大限制。结果nwidth为负数。
可改为
nwidth = (nwidth > 2009||nwidth < 0) ? 2009 : nwidth;
( sambatree 发表于 2003-3-26 14:26:00)
㈦ 关于ADO封装的问题!急。
一个常用的对数据层访问的模式为DAO(data access object)
将所有与数据库相关的代码(如:sql, recordset , connection等)都封装到类中,系统中要用到数据库操作的地方就调用 类.
至于类如何分类分,有2种基本的方法:
1.所有数据库相关的代码放到一个类中,这种方法会使类非常庞大,不建议使用
2.一个表一个类,例如数据库中有个STUDENT表,我们写一个StudentDao类,其中包括对STUDENT表的所有操作,如包括insert(), delete(), search(),update()等函数,这些函数中就放数据库操作的相关代码.这种方法很常用.
㈧ MFC下开发的程序,使用ado方法连接SQL SERVER数据库的那段连接代码 跪求啊。。。。
首先确认你的用户名和密码正确和数据库服务器默认实例已经正常启动;然后你试试下面的连接字_bstr_t strConnect="Provider=SQLOLEDB.1;DataSource=(local);InitialCatalog=StuDB; User ID="sa";Password="sa";连接本地数据库服务器默认实例有三种方式:(local)、LocalHost、127.0.0.1。所以,local要加括号或者改用LocalHost;还有你的初始编目中是指定数据库名称而不是数据库文件的名称,所以StuDB_Data.MDF改为StuDB(请确认你建立的数据库名称为StuDB)。然后你再试一试,如果还不行,请将错误贴上!
㈨ 求在visual studio 环境中用VC++ 实现一个小工程,并用ADO连接数据库。
生成应用程序框架并初始化OLE/COM库环境
创建一个标准的MFC AppWizard(exe)应用程序,然后在使用ADO数据库的InitInstance函数中初始化OLE/COM库(因为ADO库是一个COM DLL库)。
本例为:
BOOL CAdotestDlg::OnInitDialog()
{
::CoInitialize(NULL); //初始化OLE/COM库环境
}
程序最后要调用 ::CoUninitialize();//释放程序占用的COM 资源。
另外:
m_pRecordset->Close(); 注意!!!不要多次关闭!!!!!!!!!!!!
m_pConnection->Close();
m_pRecordset = NULL;
m_pConnection = NULL;
2. 引入ADO库文件
使用ADO前必须在工程的stdafx.h文件最后用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下:
#import "C:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
ADO类的定义是作为一种资源存储在ADO DLL(msado15.dll)中,在其内部称为类型库。类型库描述了自治接口,以及C++使用的COM vtable接口。当使用#import指令时,在运行时Visual C++需要从ADO DLL中读取这个类型库,并以此创建一组C++头文件。这些头文件具有.tli 和.tlh扩展名,读者可以在项目的目录下找到这两个文件。在C++程序代码中调用的ADO类要在这些文件中定义。
程序的第三行指示ADO对象不使用名称空间。在有些应用程序中,由于应用程序中的对象与ADO中的对象之间可能会出现命名冲突,所以有必要使用名称空间。如果要使用名称空间,则可把第三行程序修改为: rename_namespace("AdoNS")。第四行代码将ADO中的EOF(文件结束)更名为adoEOF,以避免与定义了自己的EOF的其他库冲突。
3.利用智能指针进行数据库操作
在CaboutDlg头文件中定义两个ADO智能指针类实例,并在对话框中加入一个ListCtrl。
class CAdotestDlg : public CDialog
{
_ConnectionPtr m_pConnection;
_RecordsetPtr m_pRecordset;
ClistCtrl m_List;
......
}
ADO库包含三个智能指针:_ConnectionPtr、_CommandPtr和_RecordsetPtr。
_ConnectionPtr通常被用来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。
_CommandPtr返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。 _RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定、游标控制等。
在使用ADO程序的事件响应中OnButton1加入以下代码:
void CAdotestDlg::OnButton1()
{
m_List.ResetContent();
m_pConnection.CreateInstance(_uuidof(Connection)); //初始化Connection指针
m_pRecordset.CreateInstance(_uuidof(Recordset));//初始化Recordset指针
try
{
m_pConnection->Open("DSN=ADOTest","","",0); //连接叫作ADOTest的ODBC数据源
//注意:这是连接不需要用户ID或密码的open 函数
// 否则形式为 ->Open("DSN=test;uid=sa;pwd=123;","","",0);
// 执行SQL语句得到一个记录集把其指针赋值给m_pRecordset
CString strSql="select * from middle";
BSTR bstrSQL = strSql.AllocSysString();
m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,adCmdText);
//adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句
while(!m_pRecordset->adoEOF)//遍历所有记录
{
//取纪录字段值方式之一
_variant_t TheValue; //VARIANT数据类型
TheValue = m_pRecordset->GetCollect("BIG_NAME");//得到字段BIG_NAME的值
if(TheValue.vt!=VT_NULL)
m_List.AddString((char*)_bstr_t(TheValue));
//将该值加入到列表控件中
//取纪录字段值方式之二
// _bstr_t TheValue1=m_pRecordset->Fields->GetItem("BIG_NAME")->Value;
// CString temp=TheValue1.();
// m_List.AddString(temp);
//数据类型转换
_variant_t vUsername,vBirthday,vID,vOld;
TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",
vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday);
m_pRecordset->MoveNext();//转到下一条纪录
}
m_pRecordset->Close();
m_pConnection->Close();
}
catch (_com_error e)//异常处理
{
AfxMessageBox(e.ErrorMessage());
}
m_pRecordset->Close(); //注意!!!不要多次关闭!!!!否则会出错
m_pConnection->Close();
m_pRecordset = NULL;
m_pConnection = NULL;
}
程序中通过_variant_t和_bstr_t转换COM对象和C++类型的数据, _variant_t类封装了OLE自治VARIANT数据类型。在C++中使用_variant_t类要比直接使用VARIANT数据类型容易得多。
好,编译后该程序就能运行了,但记住运行前要创建一个叫ADOTest的ODBC数据源。该程序将把表middle中的BIG_NAME字段值显示在列表控件中。
㈩ 怎么在VC++中用ADO创建数据库
VC++中使用ADO方式操作ACCESS数据库是Microsoft数据库应用程序开发的新接口,是建立在OLEDB之上的高层数据库访问技术,即使你对OLEDB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBCAPI、DAO、RDO都要容易使用,并不失灵活性。本文详细地介绍在VisualC++开发环境下如何使用ADO来进行数据库应用程序开发,并给出示例代码。为了使读者朋友都能测试本例提供的代码,我们采用Access数据库,您可以直接在我们提供的示例代码中找到这个test。mdb。程序编译运行后的效果一所示:
一、实现方法
万事开头难,任何一种新技术对于初学者来说最重要的还是"入门",掌握其要点。让我们来看看ADO数据库开发的基本流程吧!它的基本步骤如下:
(1)初始化COM库,引入ADO库定义文件
(2)用Connection对象连接数据库
(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。
(4)使用完毕后关闭连接释放对象。
下面我们将详细介绍上述步骤并给出相关代码。
1、COM库的初始化
我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码:
BOOLCADOTest1App::InitInstance()
{
AfxOleInit();
。。。。。。
}
2、用#import指令引入ADO类型库
为了引入ADO类型库,需要在项目的stdafx。h文件中加入如下语句:
#import"c:\programfiles\commonfiles\system\ado\msado15。dll"
no_namespacerename("EOF","adoEOF")
这一语句有何作用呢?其最终作用同我们已经十分熟悉的#include类似,编译的时候系统会为我们生成msado15。tlh,ado15。tli两个C++头文件来定义ADO库。
需要读者朋友注意的是:您的开发环境中msado15。dll不一定在这个目录下,请按实际情况修改;在编译的时候可能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告:msado15。tlh(405):warningC4146:,resultstillunsigned。
3、创建Connection对象并连接数据库
为了首先我们需要添加一个指向Connection对象的指针_ConnectionPtrm_pConnection,下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉:
BOOLCADOTest1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
HRESULThr;
try
{
hr=m_pConnection。CreateInstance("ADODB。Connection");///创建Connection对象
if(SUCCEEDED(hr))
{
hr=m_pConnection->Open("Provider=Microsoft。Jet。OLEDB。4。0;
DataSource=test。mdb","","",adModeUnknown);///连接数据库
//上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,
//需要改为:Provider=Microsoft。Jet。OLEDB。3。51;
}
}
catch(_com_errore)///捕捉异常
{
CStringerrormessage;
errormessage。Format("连接数据库失败!\r\n错误信息:%s",e。ErrorMessage());
AfxMessageBox(errormessage);///显示错误信息
}
在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型:
HRESULTConnection15::Open(_bstr_tConnectionString,_bstr_tUserID,_bstr_tPassword,longOptions);
上述函数中参数ConnectionString为连接字串;参数UserID是用户名;参数Password是登陆密码;参数Options是连接选项,用于指定Connection对象对数据的更新许可权,一般情况下Options可以是如下几个常量:
adModeUnknown:缺省。当前的许可权未设置