数据库ADO编程

来源:互联网 发布:淘宝怎么发视频给买家 编辑:程序博客网 时间:2024/06/18 06:18

一、数据库操作准备
1、导入ADO动态链接库

在工程的stdafx.h中加入如下语句:
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace\

rename("EOF","adoEOF")


这一语句有何作用呢?其最终作用同我们熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh和ado15.tli两个C++头文件来定义ADO库,即加载ADO动态库(msado15.dll)。

其中,no_namespace表明不使用命名空间,rename("EOF","adoEOF")表明把ADO中用到的EOF改为adoEOF,防止发生命名冲突。

注意:该代码需要在一行中完成,如果写成两行或者多行,行末要加上“\”符号,表示把这几行看成一行,如本例。
2、初始化OLE/COM库环境

在基于MFC的应用里,初始化OLE/COM库环境的一个比较好的位置是在应用类的InitInstance成员函数中,而且直接使用AfxOleInit,在退出应用时,该函数也负责COM资源的释放,将此函数添加在InitInstance中的如下位置:

BOOL CExpApp::InitInstance()
{
AfxEnableControlContainer();
//初始化OLE DLLs
if(!AfxOleInit())
{
AfxMessageBox("初始化OLE DLL失败!");
Return FALSE;
}
......
}

说明:也可以在InitInstance中使用::CoInitialize初始化OLE/COM库环境,但须在ExitInitInstance中使用::CoUninitialize释放占用的COM资源,显然使用AfxOleInit更为方便。
3、连接数据库

在Doc\View程序中,通常在应用类(App类)中进行数据库的连接

1)声明一个Connection指针

_ConnectionPtr m_pConnection;

注:ADO最重要的三个对象有三个:连接对象(Connection)、命令对象(Command)和记录集对象(RecordSet)。在使用这三个对象的时候,需要定义与之相对应的智能指针,分别为_ConnectionPtr、_CommandPtr、_RecordsetPtr

由上述ConnectionPtr指针的使用步骤可知,和C++中的类指针使用方法一样,智能指针也要先定义指针变量、创建其实例(实例化),然后就可以调用它的方法和属性。不同的是,该智能指针最后是自动进行内存释放的

所有的智能指针都是基于_com_ptr_t模板类的,该类封装了IUnknow接口的3个方法:QueryInterface、Addref和Release。它具有自动计数的机制,即在构造对象时,自动为该对象计数加1。析构对象时,自动调用Release方法。(即该类型的指针在使用后不需要手动释放内存)(但需要调用Close方法,关闭连接或者记录集)所以智能指针会使代码更加简洁并且不易出错。

2)创建Connection对象

m_pConnection.CreateInstance(__uuidof(Connection));

m_pConnection.CreateInstance("ADODB.Connection");

上述两种方法均可。

注意:上面调用_ConnectionPtr接口指针的方法CreateInstance时,用的是“.”而非

“->”。

3)设置连接字符串,以便指定需要的连接

3.1) 使用JET数据库引擎实现对Acess2000类型的数据库info.mdb的连接

CString strSQL="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=info.mdb;User ID=admin;Passward=;";

或者

CString strSQL=_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=info.mdb;User ID=admin;Passward=;");

3.2) 使用OLE DB提供者实现对SQL Server的标准安全连接串

strConnect=_T("Provider=sqloledb;Data Source=MyServerName;"

"Initial Catalog=MyDateBaseName;"

"User ID=MyUserName;Password=MyPassword;");

例程:strConnection="Provider=SQLOLEDB;DataSource=local;InitialCatalog=DVDRentDB_Data.MDF;"

"User ID=sa;Password=820415";

m_pConnection->Open((_bstr_t)strSQL,"","",adModeUnknown);

或者是在此处不设置User ID和Password,而直接在Open的第2、3个参数中设置。

strConnection="Provider=SQLOLEDB;DataSource=local;InitialCatalog=DVDRentDB_Data.MDF";

m_pConnection->Open((_bstr_t)strSQL,"sa","820415",adModeUnknown);

注意:上面设置连接字符串的时候,如果过长需要分行时,则每一行都要加上双引号,在最后加上分号即可。

如果是本地服务器,则DataSource=local或本地服务器名均可

若数据库没有设置密码,在连接字符串中可以将其省略,但User ID不能省

若数据库和程序文件不在同一文件夹下,直接写数据库名即可,在InitialCatalog中不需加上该数据库的存储器地址

3.3) 使用OLE DB提供者实现对远程SQL Server的标准安全连接串

strConnect=_T("Provider=sqloledb;Network Library=DBMSSOCN;"

"Data Source=130.120.110.001,1433;"

"Initial Catalog=MyDateBaseName;"

"User ID=MyUserName;Password=MyPassword;");

4)、使用m_pConnection的Open方法实现对数据库的连接

在ADO的操作中建议使用try...catch( )来捕获错误信息,因为它有时会经常出现一些意想不到的错误

try
{
m_pConnection->Open( (_bstr_t) strSQL," "," ",adModeUnknown);
}
catch(_com_error e) //捕捉异常
{
CString strError;
strError.Format( "连接数据库发生异常! \r \n错误信息:%s",e.ErrorMessage( ) );
AfxMessageBox(errormessage); //显示错误信息

}

4、关闭连接

一般重载App类的ExitInstace( )函数实现

调用m_pConnection的Close方法关闭连接即可

m_pConnection->Close( );

m_pConnection=NULL;

注意:由于初始化COM库调用的是AfxOleInit,这种方法初始化COM库的优点就在于资源 的释放也是自动进行的,所以不必担心资源泄漏的问题。

二、数据库操作

ADO库中包含的三个基本接口为_ConnectionPtr接口、_CommandPtr接口、_RecordsetPtr接口。

1、_ConnectionPtr接口

该接口返回一个记录集或一个空指针

通常用它来创建一个数据库连接,或执行一条不返回任何结果的SQL语句,如一个存储过程。

不推荐使用_ConnectionPtr接口返回一个记录集,对于要返回记录集的操作通常用_RecordsetPtr来实现,而且使用_ConnectionPtr时要想得到记录数目必须遍历所有记录,但使用_RecordsetPtr时则不需要。

2、_CommandPtr接口

该接口返回一个记录集。

它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。

在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr

接口里直接使用连接串。如果只执行一次或者几次数据库访问操作,后者是比较好的选择。但是,如果频繁访问数据库,并要返回很多记录集,那么应该使用全局_ConnectionPtr接口创建一个数据库连接,然后使用_CommandPtr接口执行存储过程和SQL语句

3、_RecordsetPtr接口

该接口是一个记录集对象。

与前两种对象相比,它对记录集提供了更多的控制功能,如记录锁定、游标控制等。同_CommandPtr接口一样,它不一定要使用一个已经创建的数据库连接,可以用一个连接串代替连接指针赋给_RecordsetPtr的connection成员变量,让它自己创建数据库连接。如果使用多个记录集,最好的方法是同Command对象一样使用已经创建了数据连接的全局_ConnectionPtr接口,然后使用_RecordsetPtr执行存储过程和SQL语句。

注意:可以使用Recordset对象来执行查询命令,但如果查询或者存储过程是需要参数的,这时就只能使用Command对象

使用Recordset对象操作数据库:

假定已经成功使用Connection对象创建了数据源的连接,连接指针为m_pConnection。

1)创建记录集

声明记录集指针 _RecordsetPtr m_pRecordset;

创建记录集 m_pRecordset.CreateInstance(__uuidof(Recordset));

2)打开记录集

记录集指针创建完毕后,调用该指针的Open方法打开记录集。

该函数声明如下:

HRESULT Recordset15::Open ( const _variant_t & Source,

const _variant_t & ActiveConnection,

enum CursorTypeEnum CursorType,

enum LockTypeEnum LockType,

long Options ) ;

各个参数的含义如下:

参数Source:为_variant_t类型的引用,可以为有效的Command对象、SQL语句表名、存储过程调用等。

参数ActiveConnection:为_variant_t类型的引用,为已经建立好的连接

参数CursorType:用于设置在打开Recordset时提供者应使用的游标类型,它可取CursorTypeEnum 中的任一值,默认值为adOpenForwardOnly。

参数 LockType:用于设置在打开Recordset时提供者应使用的锁定类型,它可取枚举LockTypeEnum中的任一值,默认值为adLockReadOnly。

参数 Options:用于设置获取Source(即Open第一个参数)的方式,其类型long。

 

 

例程1: CString strSQL = "select * from mytablename";

m_pRecordset->Open ( _variant_t (strSQL),

m_pConnection.GetInterfacePtr( ),
adOpenDynamic,
adLockOptimistic,
adCmdText ) ;

使用SQL语句作为Open方法的第一个参数Source的值,此时Options为adCmdText

例程2: m_pRecordset->Open ( _variant_t ("tbDVDInfo") ),

m_pConnection.GetInterfacePtr( ),
adOpenDynamic,
adLockOptimistic,
adCmdTable ) ;

直接使用表名作为第一个参数,此时Options应为adCmdTable

3)遍历记录集

一般在返回记录集时,通常要遍历结果记录集,以便查看或编辑某一条记录,Recoreset指针提供了几个用于实现遍历的方法。

注意:为了避免发生异常,一般在使用MoveFirst、MovePrev之前,需要使用记录集的指针BOF属性来检测当前的记录集指针是否位于第一条记录之前;在使用MoveLast、MoveNext之前需要使用记录集指针的EOF属性来检测当前的记录集指针是否位于最后一条记录之后

4)记录集定位

记录集接口类提供了两种定位方法:绝对定位和书签定位

前者通过设置或者获取AbsolutePosition属性即可,其值从1开始,并且当前记录为记录集中第一条记录时等于1

对于后者可以通过设置或获取BookMark属性即可

5)访问记录集

最简单的方法是直接使用如下语句:

m_pRecordset->GetCollect (字段名);

设置字段值

m_pRecordset->PutCollect (字段名,新值);

两个方法的原型:

_variant_t GetCollect ( const _variant_t & Index )

void PutCollect ( const _variant_t & Index , const _variant_t &pvar )

其中:参数Index可以是字符串表示字段名,也可以是整型,表示字段对应的序号。

pvar表示要写入的变量值。

例如:

_variant_t var;

var=m_pRentRecordset->GetCollect("ID");
var=m_pRentRecordset->GetCollect(long(0)); 都可以

6)记录集更新

更新记录集包括添加新的记录、编辑当前记录和删除当前记录

记录集接口指针对这三种操作分别提供了相应的方法

添加新的记录:AddNew

编辑当前记录:Edit

删除当前记录:Delete

注意:记录集接口指针针对AddNew以及Edit方法提供了Update方法,用于在数据库中更新新添加或者编辑后的记录

AddNew方法:用于添加新纪录(该添加是直接在表的末尾续加的),该方法可以使用参数,在参数中指定要添加的新纪录;也可以不使用参数,而在后面使用PutCollect方法,并需使用Update函数保存新纪录。

Update方法:用于保存从调用AddNew方法以来所作的任何更改。

//++++++++++++++++++++++++++++++++++++++++++
//在表的末尾增加新纪录
m_pRecordset->AddNew();//不加就是修改
//++++++++++++++++++++++++++++++++++++++++++
m_pRecordset->PutCollect("姓名",_variant_t(m_strName));

m_pRecordset->PutCollect("工作单位",_variant_t(m_strComName));
m_pRecordset->PutCollect("单位地址",_variant_t(m_strComAddr));

//更新数据库-将新纪录存入数据库
m_pRecordset->Update();

Edit的方法:

Delete的方法:

7)记录集关闭

在对记录集的操作完成后,必须及时关闭记录集

if ( m_pRecordset != NULL )

{

m_pRecordset ->Close( );

m_pRecordset =NULL;

}

三、使用ADO编程常见问题解答

  以下均是针对MS SQL 7.0编程时所遇问题进行讨论。

  1、连接失败可能原因

  Enterprise Managemer内,打开将服务器的属性对话框,在Security选项卡中,有一个选项Authentication。

  如果该选项是Windows NT only,则你的程序所用的连接字符串就一定要包含Trusted_Connection参数,并且其值必须为yes,如:

"Provider=SQLOLEDB;Server=888;Trusted_Connection=yes"
";Database=master;uid=lad;";
  如果不按上述操作,程序运行时连接必然失败。

  如果Authentication选项是SQL Server and Windows NT,则你的程序所用的连接字符串可以不包含Trusted_Connection参数,如:

"Provider=SQLOLEDB;Server=888;Database=master;uid=lad;pwd=111;";
  因为ADO给该参数取的默认值就是no,所以可以省略。我认为还是取默认值比较安全一些。

  2、改变当前数据库的方法

  使用Tansct-SQL中的USE语句即可。

  3、如何判断一个数据库是否存在

  (1)、可打开master数据库中一个叫做SCHEMATA的视图,其内容列出了该服务器上所有的数据库名称。

  (2) 、更简便的方法是使用USE语句,成功了就存在;不成功,就不存在。例如:

try{
m_pConnect->Execute ( _bstr_t("USE INSURANCE_2002"),NULL,
adCmdText│adExecuteNoRecords );
}
catch (_com_error &e)
{
blSuccess=FALSE;
CString str="数据库INSURANCE_2002不存在!\n";
str+=e.Description();
::MessageBox(NULL,str,"警告",MB_OK │ MB_ICONWARNING);
}
  4、判断一个表是否存在

  (1)、同样判断一个表是否存在,也可以用是否成功地打开它来判断,十分方便,例如:

try{
m_pRecordset->Open(_variant_t("mytable"),
_variant_t((IDispatch *)m_pConnection,true), adOpenKeyset,
adLockOptimistic, adCmdTable);
}
catch (_com_error &e)
{
::MessageBox(NULL,"该表不存在。","提示",MB_OK │ MB_ICONWARNING);
}
  (2)、要不然可以采用麻烦一点的办法,就是在MS-SQL服务器上的每个数据库中都有一个名为sysobjects的表,查看此表的内容即知指定的表是否在该数据库中。

  (3)、同样,每个数据库中都有一个名为TABLES的视图(View),查看此视图的内容即知指定的表是否在该数据库中。

  5、类型转换问题

  (1)、类型VARIANT_BOOL

  类型VARIANT_BOOL等价于short类型。The VARIANT_BOOL is equivalent to short. see it's definition below:
typdef short VARIANT_BOOL

  (2)、_com_ptr_t类的类型转换

  _ConnectionPtr可以自动转换成IDspatch*类型,这是因为_ConnectionPtr实际上是_com_ptr_t类的一个实例,而这个类有此类型转换函数。

  同理,_RecordsetPtr和_CommandPtr也都可以这样转换。

  (3)、_bstr_t和_variant_t类

  在ADO编程时,_bstr_t和_variant_t这两个类很有用,省去了许多BSTR和VARIANT类型转换的麻烦。

  6、打开记录集时的问题

  在打开记录集时,在调用Recordset的Open方法时,其最后一个参数里一定不能包含adAsyncExecute,否则将因为是异步操作,在读取数据时无法读到数据。

  7、异常处理问题

  对所有调用ADO的语句一定要用try和catch语句捕捉异常,否则在发生异常时,程序会异常退出。

  8、使用SafeArray问题

  在初学使用中,我曾遇到一个伤脑筋的问题,一定要注意:

  在定义了SAFEARRAY的指针后,如果打算重复使用多次,则在中间可以调用::SafeArrayDestroyData释放数据,但决不能调用::SafeArrayDestroyDescriptor,否则必然出错,即使调用SafeArrayCreate也不行。例如:

SAFEARRAY *psa;
......
//When the data are no longer to be used:
::SafeArrayDestroyData( psa);
  我分析在定义psa指针时,一个SAFEARRAY的实例(也就是SAFEARRAY描述符)也同时被自动建立了。但是只要一调用::SafeArrayDestroyDescriptor,描述符就被销毁了。

  所以我认为::SafeArrayDestroyDescriptor可以根本就不调用,即使调用也必须在最后调用。

  9、重复使用命令对象问题

  一个命令对象如果要重复使用多次(尤其是带参数的命令),则在第一次执行之前,应将它的Prepared属性设置为TRUE。这样会使第一次执行减慢,但却可以使以后的执行全部加快。

  10、绑定字符串型字段问题

  如果要绑定的字段是字符串类型,则对应的字符数组的元素个数一定要比字段长度大2(比如m_szau_fname[22],其绑定的字段au_fname的长度实际是20),不这样绑定就会失败。

  11、使用AppendChunk的问题

  当用AddNew方法刚刚向记录集内添加一个新记录之后,不能首先向一个长数据字段(image类型)写入数据,必须先向其他字段写入过数据之后,才能调用AppendChunk写该字段,否则出错。也就是说,AppendChunk不能紧接在AddNew之后。另外,写入其他字段后还必须紧接着调用AppendChunk,而不能调用记录集的Update方法后,才调用AppendChunk,否则调用AppendChunk时也会出错。换句话说,就是必须AppendChunk在前,Update在后。因而这个时候就不能使用带参数的AddNew了,因为带参数的AddNew会自动调用记录集的Update,所以AppendChunk就跑到Update的后面了,就只有出错了!因此,这时应该用不带参数的AddNew。

  我推测这可能是MS SQL 7.0的问题,在MS SQL 2000中则不存在这些问题,但是AppendChunk仍然不能在Update之后。

  四、小结

  一般情况下,Connection和Command的Execute用于执行不产生记录集的命令,而Recordset的Open用于产生一个记录集,当然也不是绝对的。特别Command主要是用于执行参数化的命令,可以直接由Command对象执行,也可以将Command对象传递给Recordset的Open。

  本文中的代码片断均在VC++ 6.0、Windows NT 4.0 SP6和MS SQL 7.0中调试通过。相信您读过之后,编写简单的数据库程序应该没有问题了。当然要编写比较实用的、复杂一点的程序,还需要对OLE DB、ADO以及数据库平台再多了解一点,希望您继续努力,一定会很快成功的!详细参考资料请参见微软MSDN July 2000光盘或MS SQL 7.0在线文档资料(Books online)。文中难免有错误和不妥之处,敬请各位批评指正!

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 眼睛得了麦粒疹怎么办 公告牌证作废怎么办 宝宝吃了护臀膏怎么办 八字五行同类强怎么办? 重装系统出现两个系统盘怎么办 看视频手机发烫怎么办 dhcp获取ip失败怎么办 苹果笔记本打不出数字怎么办 系统盘读不出来怎么办 waifai密码忘了怎么办 window xp忘记密码怎么办 winxp密码忘了怎么办 xp电脑密码忘记怎么办 xp桌面图标有阴影怎么办 联想电脑忘记登录密码怎么办 xp系统忘掉密码怎么办 xp密码忘记了怎么办 电脑忘记开机密码怎么办 注销了win7用户名怎么办 xp启动后黑屏怎么办 开机要按esc怎么办 excel产品激活失败怎么办 米粉卡激活失败怎么办 电脑死机ppt保存怎么办 电脑装系统黑屏怎么办 电脑c盘无法访问怎么办 d盘变成ntfs怎么办 系统重装卡住了怎么办 系统关机没反应怎么办 232串口打开失败怎么办 逆水寒cpu不支持怎么办 显卡被禁用了怎么办 vmvare注册错了怎么办 电脑主机未成功启动怎么办 主机未成功启动怎么办 电脑主机未能成功启动怎么办 虚拟机没有自带怎么办 错误连接为720怎么办 dns错误不能上网怎么办 家里无线用不了怎么办 磁盘c5坏了怎么办