下载

1下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 VC+ADO+ORACLE开发

VC+ADO+ORACLE开发.doc

VC+ADO+ORACLE开发

wangyanping_918
2012-10-08 0人阅读 举报 0 0 暂无简介

简介:本文档为《VC+ADO+ORACLE开发doc》,可适用于IT/计算机领域

ADO简介      ADO是Microsoft为最新和最强大的数据访问范例OLEDB而设计的是一个便于使用的应用程序层接口。ADO使您能够编写应用程序以通过OLEDB提供者访问和操作数据库服务器中的数据。ADO最主要的优点是易于使用、速度快、内存支出少和磁盘遗迹小。ADO在关键的应用方案中使用最少的网络流量并且在前端和数据源之间使用最少的层数所有这些都是为了提供轻量、高性能的接口。之所以称为ADO是用了一个比较熟悉的暗喻OLE自动化接口。      OLEDB是一组”组件对象模型”(COM)接口是新的数据库低层接口它封装了ODBC的功能并以统一的方式访问存储在不同信息源中的数据。OLEDB是MicrosoftUDA(UniversalDataAccess)策略的技术基础。OLEDB为任何数据源提供了高性能的访问这些数据源包括关系和非关系数据库、电子邮件和文件系统、文本和图形、自定义业务对象等等。也就是说OLEDB并不局限于ISAM、Jet甚至关系数据源它能够处理任何类型的数据而不考虑它们的格式和存储方法。在实际应用中这种多样性意味着可以访问驻留在Excel电子数据表、文本文件、电子邮件目录服务甚至邮件服务器诸如MicrosoftExchange中的数据。但是OLEDB应用程序编程接口的目的是为各种应用程序提供最佳的功能它并不符合简单化的要求。您需要的API应该是一座连接应用程序和OLEDB的桥梁这就是ActiveXDataObjects(ADO)。    从前一直以为用ADO编写的程序可以做到大致通用但是经过这次项目让我明白你必须事先确定好应用将要使用的数据库并且了解其特性否则最终你的程序肯定会折磨你就像本次项目我为了方便一开始使用了access数据库并且顺利的开发完程序然后当我移植到ORACLE的时候出现了许多莫名其妙的问题如时间取不出来、select语句无效、记录集不更新、程序莫名其妙异常等问题然而该程序用MSSQLSERVER也是正常的。我相当郁闷为什么会这样呢?实际后来我明白一部分原因是ORACLE特性所致另一部分原因是ADO所致。通用的开发步骤)引入ADO库文件  使用ADO前必须在工程的stdafxh头文件里用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下所示:     加入ADO支持库    #import"c:programfilescommonfilessystemadomsadodll"    nonamespace    rename("EOF","adoEOF")      加载的位置如下图:  这行语句声明在工程中使用ADO但不使用ADO的名字空间并且为了避免常数冲突将常数EOF改名为adoEOF。现在不需添加另外的头文件就可以使用ADO接口了。、初始化OLECOM库环境  必须注意的是ADO库是一组COM动态库这意味应用程序在调用ADO前必须初始化OLECOM库环境。在MFC应用程序里一个比较好的方法是在应用程序主类的InitInstance成员函数里初始化OLECOM库环境。BOOLCMyInstanceAppApp::InitInstance(){if(!AfxOleInit())这就是初始化COM库{AfxMessageBox(“OLE初始化出错!”)returnFALSE}……}初始化COM库也可以用如下代码:HRESULThr  初始化Com库 hr=CoInitialize() if(FAILED(hr)) {  AfxMessageBox(T("初始化Com库失败!"))   }、ADO接口简介  ADO库包含三个基本接口:ConnectionPtr接口、CommandPtr接口和RecordsetPtr接口。  ConnectionPtr接口返回一个记录集或一个空指针。通常使用它来创建一个数据连接或执行一条不返回任何结果的SQL语句如一个存储过程。使用ConnectionPtr接口返回一个记录集不是一个好的使用方法。对于要返回记录的操作通常用RecordserPtr来实现。而用ConnectionPtr操作时要想得到记录条数得遍历所有记录而用RecordserPtr时不需要。  CommandPtr接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用CommandPtr接口时你可以利用全局ConnectionPtr接口也可以在CommandPtr接口里直接使用连接串。如果你只执行一次或几次数据访问操作后者是比较好的选择。但如果你要频繁访问数据库并要返回很多记录集那么你应该使用全局ConnectionPtr接口创建一个数据连接然后使用CommandPtr接口执行存储过程和SQL语句。  RecordsetPtr是一个记录集对象。与以上两种对象相比它对记录集提供了更多的控制功能如记录锁定游标控制等。同CommandPtr接口一样它不一定要使用一个已经创建的数据连接可以用一个连接串代替连接指针赋给RecordsetPtr的connection成员变量让它自己创建数据连接。如果你要使用多个记录集最好的方法是同Command对象一样使用已经创建了数据连接的全局ConnectionPtr接口然后使用RecordsetPtr执行存储过程和SQL语句。 通用数据库连接串及区别     通常情况下ORACLE有两种连接方式:第一种方式:OLEDBProviderforOracle(fromMicrosoft)TheMicrosoftOLEDBProviderforOracleallowsADOtoaccessOracledatabasesstrConnect=T("<SPAN>    "<SPAN>)Formoreinformation,see:MicrosoftOLEDBProviderforOracle第二种方式:OLEDBProviderforOracle(fromOracle)ForStandardsecurity:strConnect=T("<SPAN>        "<SPAN>)·ForaTrustedconnection:·OSAuthenticatedconnectsettinguserIDto"":·strConnect=T("<SPAN>  "<SPAN>)·OSAuthenticatedconnectusingOSAuthent:strConnect=T("<SPAN>)Note:"<CODE>Formoreinformation,see:OracleProviderforOLEDBDeveloper'sGuide重要提示:       第一种连接方式不支持select*from“表”语句的表中带有时间戳和blob字段。即如果某表中带有时间戳或者blob字段则select*语句会出错。改进的办法为将所有字段写出来如果遇上时间戳字段则进行如下处理:select  字段字段tochar(时间字段,'yyyymmdd  hh:mi:ss') 时间字段别名字段 from  tablename如果遇上blob字段可以用selectwhere语句找到记录然后用updae语句更新。       第二种方式语句支持select*语句在程序上可以做到不少简化。常见错误:网上不少朋友说在用ORACLE的时候记录集open出错实际情况大致有两种一种就是连接字符串用了第一种另一种方式就是定义表结构的时候里面用到了ORACLE的关键字段。常见疑问:有些人问Provider为什么有时候是OraOLEDBOracle有时候是OraOLEDBOracle其实这个问题比较简单用regedit打开注册表查询一下即可如果你的机器上两种描述都有则用其中任何一个即可如果仅仅有一种名称那么程序中使用注册表中描述的名称即可。不过需要指出的是如果你的机器上使用的不是企业版的ORACLE而是gEXPRESS则当该数据库安装了客户端后有时候会修改注册表中连接串描述导致连接字符串无效。但是不安装客户端仅仅安装服务器程序就不会出问题。 记录集的常用open方式及区别      关于记录集的描述网上已经有很多详细的描述在此不再累赘需要指出的是那些open的参数在其他数据库中似乎从记录集的结果中看区别不是很大。但是在ORACLE中区别则较大甚至会影响我们的程序。说明如下:mpKnowRecordsetCreateInstance(uuidof(Recordset))第一步应该将记录集实例化CStringsSqlsSql="SELECT*FROMInstanceInfoWHEREPropType="第二步生成相应的select语句try {   第三步打开记录集  mpKnowRecordset>Open((variantt)sSql,   theAppmptrConnectionGetInterfacePtr(),   adOpenDynamic,>也可以改为adOpenStatic adLockOptimistic,   adCmdText) } catch(comerror*e) {  AfxMessageBox(e>ErrorMessage()) }if(mpParaRecordset>State) {  mpParaRecordset>Close()第四步使用结束应该关闭记录集  mpParaRecordset= }上述是记录集常见使用步骤大家应该比较熟悉但要特别申明如下:)如果open方式使用的是adOpenDynamic则如果程序中增加了记录并且mpKnowRecordset>Update()后该记录尽管会实时的添加到ORACLE数据库中但是mpKnowRecordset中并没有新添加的记录只有使用mpKnowRecordset>Requery()语句才能将新增加的记录添加到mpKnowRecordset。网上很多人说记录集的记录没有增加到list中或者只有重新登录系统才能看到新增加的记录即属于此种情况。如果open方式使用的是adOpenStatic则中仅仅使用mpKnowRecordset>Update()即可将增加的记录实时的反应到mpKnowRecordset中。)上述情况在其他数据库中没有太大区别。因此如果大家如果在ORACLE中在记录集中出现问题不妨首先调整一下open参数不要马上修改主程序。或许你会得到意外惊喜! oracle对的个性处理以及ado中对update的影响假设我们再ORACLE数据库中已经定义了TEST表表中有两个字段字段分别为A,NUMBERAVARCHER()事先我们在数据库中增加一个记录{“aa”}并且仅一条记录下面的代码可以重现错误。以下是测试的代码CString sSql="SELECT*FROMTEST" try {   mpKnowRecordset>Open((variantt)sSql,   theAppmptrConnectionGetInterfacePtr(),   adOpenStatic,   adLockOptimistic,   adCmdText) } catch(comerror*e) {  AfxMessageBox(e>ErrorMessage()) } mpKnowRecordset>MoveFirst()假设数据库中仅仅一条记录切记 varianttvar var=mpKnowRecordset>GetCollect("A") if(varvt!=VT)如果数据库中该条记录的A字段有数据 {  CStringstrTemp=(LPCSTR)bstrt(var)    CStringmsA  msA=""  mpKnowRecordset>PutCollect("A",(variantt)msA)  mpKnowRecordset>Update()第一次update  mpKnowRecordset>Requery()没有这句话绝对在第二个update处出错  msA="哈哈"  mpKnowRecordset>PutCollect("A",(variantt)msA)  mpKnowRecordset>Update()该处可能出错 } else)如果数据库中该条记录的A字段没有数据 {  CStringmsA  msA=""  mpKnowRecordset>PutCollect("A",(variantt)msA)  mpKnowRecordset>Update()()第一次update  mpKnowRecordset>Requery()没有这句话绝对在第二个update处出错  msA=""  mpKnowRecordset>PutCollect("A",(variantt)msA)  mpKnowRecordset>Update()该处可能出错 }测试代码结束      为什么会出现上述错误呢?在第二个的更新语句执行中在代码中检测到错误。核心问题的原因在于Oracle引擎将空的字符串转换为而不是“”。ADO记录集存储一个零长度缓冲区为空字符串。     因此如果大家程序中遇上一条记录多次更新出错的情况应该首先考虑对“”空字符串的处理多加一些逻辑判断或者更新update的时候增加Requery。    上述问题在其他数据库中比较正常。 timestamp字段的建议     时间戳在ORACLE中是比较个性的问题在数据库中如果有timestamp字段会给我们程序移植(移植到其他数据库)带来诸多问题建议大家慎用或者将时间改为字符串或者将时间改为number。   )如果是时间戳应该进行如下程序的修改:    插入:insert  into  tablename(,时间字段名 )  values(,sysdate)    检索:select  tochar(时间字段,'yyyymmdd  hh:mi:ss')  时间字段别名from  ttable  来获得,时间格式可以自己定如果是想将某个时间insert到这个字段就用todate()insertinto表名(字段)values(todate(时间值,'yyyymmddhh:mi:ss'))时间的默认值可以在字段中设置sysdate   )将时间戳改为字符串的问题比较简单仅仅需要注意字符串格式一定要统一长度一定要定长。   )将时间改为number。建议采用的方法       需要用到的核心函数:time函数原型:timettime(timet*timer)函数功能:得到机器的日历时间或者设置日历时间函数返回:机器日历时间参数说明:timer=时得到机器日历时间timer=时间数值时用于设置日历时间timet是一个long类型。  取数据然后以时间格式展现:  var=mpKnowRecordset>GetCollect("PropDateTime")     if(varvt!=VT)     {      LONGLONGnTime=(LONGLONG)(var)      timettt=(timet)nTime      CTime*cTime=newCTime(tt)        CStringsTime=cTime>Format("YmdH:M:S")      mctrlListBaseSetItemText(i,, sTime)       deletecTime     }  将数据保存到数据库:  timetltime   time(ltime)   LONGLONGnTime=(LONGLONG)ltime   mpKnowRecordset>PutCollect("PropDatetime",(variantt)(nTime))  相关time的一些详细说明如下:      头文件<timeh>中说明了一些用于处理日期和时间的类型和函数。其中的一部分函数用于处理当地时间因为时区等原因当地时间与日历时间可能不相同。clockt和timet是两个用于表示时间的算术类型而structtm则用于存放日历时间的各个成分。tm的各个成员的用途及取值范围如下:      inttmsec*秒~*inttmmin*分~*inttmhour*时~*inttmmday*日~*inttmmon*月(从月开始)~*inttmyear*年(从年开始)*inttmwday*星期(从周日开始)~*inttmyday*天数(从月日开始)~*inttmisdst*夏令时标记*     其中tmisdst在使用夏令时时其值为正在不使用夏令时时其值为如果该信息不能使用其值为负。     clock#include<timeh>clocktclock(void)      返回程序自开始执行到目前为止所占用的处理机时间。如果处理机时间不可使用那么返回。clock()CLOCKSPERSEC是以秒为单位表示的时间。     time#include<timeh>     timettime(timet*tp)    返回当前日历时间。如果日历时间不能使用则返回。如果tp不为那么同时把返回值赋给*tp。    difftime#include<timeh>     doubledifftime(timettime,timettime)    返回timetime的值(以秒为单位)。    mktime#include<timeh>     timetmktime(structtm*tp)    将结构*tp中的当地时间转换为timet类型的日历时间并返回该时间。如果不能转换则返回。    asctime#include<timeh>      char*asctime(conststructtm*tp)     将结构*tp中的时间转换成如下所示的字符串形式:    daymonthdatehours:minutes:secondsyearn    如:       FriApr::n      返回指向该字符串的指针。字符串存储在可被其他调用重写的静态对象中。      ctime#include<timeh>       char*ctime(consttimet*tp)     将*tp中的日历时间转换为当地时间的字符串并返回指向该字符串指针。字符串存储在可被其他调用重写的静态对象中。等价于如下调用:    asctime(localtime(tp))     gmtime#include<timeh>     structtm*gmtime(consttimet*tp)      将*tp中的日历时间转换成structtm结构形式的国际标准时间(UTC)并返回指向该结构的指针。如果转换失败返回。结构内容存储在可被其他调用重写的静态对象中。     localtime#include<timeh>structtm*localtime(consttimet*tp)    将*tp中的日历时间转换成structtm结构形式的本地时间并返回指向该结构的指针。结构内容存储在可被其他调用重写的静态对象中。     strftime#include<timeh>      sizetstrftime(char*s,sizetsmax,constchar*fmt,conststructtm*tp)    根据fmt的格式说明把结构*tp中的日期与时间信息转换成指定的格式并存储到s所指向的数组中写到s中的字符数不能多于smax。函数返回实际写到s中的字符数(不包括'')如果产生的字符数多于smax则返回。    fmt类似于printf()中的格式说明它由个或多个转换规格说明与普通字符组成。普通字符原封不动的拷贝到s中每个c按照下面所描述的格式用与当地环境相适应的值来替换。转换规格列表如下:    返回当前日历时间。如果日历时间不能使用则返回。如果tp不为那么同时把返回值赋给*tp。   格式说明a  一星期中各天的缩写名A一星期中各天的全名b缩写月份名B月份全名c当地时间和日期表示d用整数表示的一个月中的第几天(~)H用整数表示的时(小时制~)I用整数表示的时(小时制~)j用整数表示的一年中各天(~)m用整数表示的月份(~)M用整数表示的分(~)p与AMPM对应的当地表示方法S用整数表示的秒(~)U用整数表示一年中的星期数(~将星期日看作为每周的第一天)w用整数表示一周中的各天(~星期日为)W用整数表示一年中的星期数(~将星期一看作为每周的第一天)x当地日期表示X当地时间表示y不带公元的年(~)Y完整年份表示Z时区名字(可获得时)本身    blob字段存取 )BLOB数据的保存BLOB类型的数据无法用普通的方式进行存储我们需要使用AppendChunk函数AppendChunk包含在Field对象中原型如下:HRESULT  AppendChunk  (const  variantt    Data  )从函数原型中可以看到关键的问题是我们需把二进制数据赋值给VARIANT类型的变量下面我们给出具体的代码并作简单的分析: 假设mpBMPBuffer指针指向一块长度为mnFileLen的二进制数据,并且已经成功打开了记录集对象mpRecordsetchar    *pBuf  =  mpBMPBufferVARIANT    varBLOBSAFEARRAY  *psaSAFEARRAYBOUND  rgsaboundmpRecordset>AddNew()          添加新记录mpRecordset>PutCollect("username",variantt("小李"))    为新记录填充username字段mpRecordset>PutCollect("old",variantt((long))          填充old字段if(pBuf){             rgsaboundlLbound  =       rgsaboundcElements  =  mnFileLen     psa  =  SafeArrayCreate(VTUI,  ,  rgsabound)                                            创建SAFEARRAY对象     for  (long  i  =    i  <  (long)mnFileLen  i)           SafeArrayPutElement  (psa,  i,  pBuf)                                                  将pBuf指向的二进制数据保存到SAFEARRAY对象psa中     varBLOBvt  =  VTARRAY  |  VTUI                                                                      将varBLOB的类型设置为BYTE类型的数组     varBLOBparray  =  psa                                                                                          为varBLOB变量赋值     mpRecordset>GetFields()>GetItem("photo")>AppendChunk(varBLOB)加入BLOB类型的数据}  mpRecordset>Update()    保存我们的数据到库中至此我们的数据已经成功地保存到了数据库中,接下来我们所要做的工作便是将该数据提取出来,让我们继续吧!)BLOB数据的读取对应于保存数据时我们所使用的AppendChunk函数读取数据应该使用GetChunk函数,GetChunk的原型如下:variantt  GetChunk  (long  Length  )给出数据的长度后GetChunk将返回包含数据的VARIANT类型变量,然后我们可以利用SafeArrayAccessData函数得到VARIANT变量中指向数据的char  *类型的指针,以方便我们的处理具体代码如下:long  lDataSize  =  mpRecordset>GetFields()>GetItem("photo")>ActualSize得到数据的长度if(lDataSize  >  ){     variantt  varBLOB     varBLOB  =  mpRecordset>GetFields()>GetItem("photo")>GetChunk(lDataSize)     if(varBLOBvt  ==  (VTARRAY  |  VTUI))                判断数据类型是否正确     {               char  *pBuf  =                 SafeArrayAccessData(varBLOBparray,(void  **)pBuf)         得到指向数据的指针               *****在这里我们可以对pBuf中的数据进行处理*****               SafeArrayUnaccessData  (varBLOBparray)     }}以上我们成功实现了BLOB数据在数据库中的存取

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/11

VC+ADO+ORACLE开发

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利