Visual C++ ADO数据库编程入门(下)
发布时间:2018-08-19 03:30:30 所属栏目:电商 来源:站长网
导读:10、邦定命据 界说一个绑定类,将其成员变量绑定到一个指定的记录集,以利便于会见记录集的字段值。 (1). 从CADORecordBinding派生出一个类: class CCustomRs : public CADORecordBinding { BEGIN_ADO_BINDING(CCustomRs) ADO_VARIABLE_LENGTH_ENTRY2(3, a
10、邦定命据 界说一个绑定类,将其成员变量绑定到一个指定的记录集,以利便于会见记录集的字段值。 (1). 从CADORecordBinding派生出一个类: class CCustomRs : public CADORecordBinding { BEGIN_ADO_BINDING(CCustomRs) ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname, sizeof(m_szau_fname), lau_fnameStatus, false) ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname, sizeof(m_szau_lname), lau_lnameStatus, false) ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szphone, sizeof(m_szphone), lphoneStatus, true) END_ADO_BINDING() public: CHAR m_szau_fname[22]; ULONG lau_fnameStatus; CHAR m_szau_lname[42]; ULONG lau_lnameStatus; CHAR m_szphone[14]; ULONG lphoneStatus; }; 个中将要绑定的字段与变量名用BEGIN_ADO_BINDING宏关联起来。每个字段对应于两个变量,一个存放字段的值,另一个存放字段的状态。字段用从1开始的序号暗示,如1,2,3等等。 出格要留意的是:假如要绑定的字段是字符串范例,则对应的字符数组的元素个数必然要比字段长度大2(好比m_szau_fname[22],其绑定的字段au_fname的长度现实是20),不这样绑定就会失败。我说明多出的2也许是为了存放字符串末了的空字符null和BSTR字符串开头的一个字(暗示BSTR的长度)。这个题目对付初学者来说也许是一个意想不到的题目。 CADORecordBinding类的界说在icrsint.h文件里,内容是: class CADORecordBinding { public: STDMETHOD_(const ADO_BINDING_ENTRY*, GetADOBindingEntries) (VOID) PURE; }; BEGIN_ADO_BINDING宏的界说也在icrsint.h文件里,内容是: #define BEGIN_ADO_BINDING(cls) public: typedef cls ADORowClass; const ADO_BINDING_ENTRY* STDMETHODCALLTYPE GetADOBindingEntries() { static const ADO_BINDING_ENTRY rgADOBindingEntries[] = { ADO_VARIABLE_LENGTH_ENTRY2宏的界说也在icrsint.h文件里: #define ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status, Modify) {Ordinal, DataType, 0, 0, Size, offsetof(ADORowClass, Buffer), offsetof(ADORowClass, Status), 0, classoffset(CADORecordBinding, ADORowClass), Modify}, #define END_ADO_BINDING宏的界说也在icrsint.h文件里: #define END_ADO_BINDING() {0, adEmpty, 0, 0, 0, 0, 0, 0, 0, FALSE}}; return rgADOBindingEntries;} (2). 绑定 _RecordsetPtr Rs1; IADORecordBinding *picRs=NULL; CCustomRs rs; ...... Rs1->QueryInterface(__uuidof(IADORecordBinding), (LPVOID*)&picRs)); picRs->BindToRecordset(&rs); 派生出的类必需通过IADORecordBinding接谈锋气绑定,挪用它的BindToRecordset要领就行了。 (3). rs中的变量等于当前记录字段的值 //Set sort and filter condition: // Step 4: Manipulate the data Rs1->Fields->GetItem("au_lname")->Properties->GetItem("Optimize")->Value = true; Rs1->Sort = "au_lname ASC"; Rs1->Filter = "phone LIKE '415 5*'"; Rs1->MoveFirst(); while (VARIANT_FALSE == Rs1->EndOfFile) { printf("Name: %st %stPhone: %sn", (rs.lau_fnameStatus == adFldOK ? rs.m_szau_fname : ""), (rs.lau_lnameStatus == adFldOK ? rs.m_szau_lname : ""), (rs.lphoneStatus == adFldOK ? rs.m_szphone : "")); if (rs.lphoneStatus == adFldOK) strcpy(rs.m_szphone, "777"); TESTHR(picRs->Update(&rs)); // Add change to the batch Rs1->MoveNext(); } Rs1->Filter = (long) adFilterNone; ...... if (picRs) picRs->Release(); Rs1->Close(); pConn->Close(); 只要字段的状态是adFldOK,就可以会见。假如修改了字段,不要忘了先挪用picRs的Update(留意不是Recordset的Update),然后才封锁,也不要忘了开释picRs(即picRs->Release();)。 (4). 此时还可以用IADORecordBinding接口添加新记载 if(FAILED(picRs->AddNew(&rs))) ...... 11. 会见长数据 在Microsoft SQL中的长数据包罗text、image等这样长范例的数据,作为二进制字节来看待。 可以用Field工具的GetChunk和AppendChunk要领来会见。每次可以读出或写入所稀有据的一部门,它会记着前次会见的位置。可是假如中间会见了此外字段后,就又得从新来了。 请看下面的例子: //写入一张照片到数据库: VARIANT varChunk; SAFEARRAY *psa; SAFEARRAYBOUND rgsabound[1]; //VT_ARRAY │ VT_UI1 CFile f("h:aaa.jpg",Cfile::modeRead); BYTE bVal[ChunkSize+1]; UINT uIsRead=0; //Create a safe array to store the array of BYTES while(1) { uIsRead=f.Read(bVal,ChunkSize); if(uIsRead==0)break; rgsabound[0].cElements =uIsRead; rgsabound[0].lLbound = 0; psa = SafeArrayCreate(VT_UI1,1,rgsabound); for(long index=0;index<uIsRead;index++) { if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index]))) ::MessageBox(NULL,"啊,又出短处了。","提醒",MB_OK │ MB_ICONWARNING); } varChunk.vt = VT_ARRAY│VT_UI1; varChunk.parray = psa; try{ m_pRecordset->Fields->GetItem("photo")->AppendChunk(varChunk); } catch (_com_error &e) { CString str=(char*)e.Description(); ::MessageBox(NULL,str+"n又出短处了。","提醒",MB_OK │ MB_ICONWARNING); } ::VariantClear(&varChunk); ::SafeArrayDestroyData( psa); if(uIsRead<ChunkSize)break; }//while(1) f.Close(); //从数据库读一张照片: CFile f; f.Open("h:bbb.jpg",Cfile::modeWrite│Cfile::modeCreate); long lPhotoSize = m_pRecordset->Fields->Item["photo"]->ActualSize; long lIsRead=0; _variant_t varChunk; BYTE buf[ChunkSize]; while(lPhotoSize>0) { lIsRead=lPhotoSize>=ChunkSize? ChunkSize:lPhotoSize; varChunk = m_pRecordset->Fields-> Item["photo"]->GetChunk(lIsRead); for(long index=0;index<lIsRead;index++) { ::SafeArrayGetElement(varChunk.parray,&index,buf+index); } f.Write(buf,lIsRead); lPhotoSize-=lIsRead; }//while() f.Close(); 12. 行使SafeArray题目 学会行使SafeArray也是很重要的,由于在ADO编程中常常要用。它的首要目标是用于automation中的数组型参数的转达。由于在收集情形中,数组是不能直接转达的,而必需将其包装成SafeArray。实质上SafeArray就是将凡是的数组增进一个描写符,声名其维数、长度、界线、元素范例等信息。SafeArray也并不光独行使,而是将其再包装到VARIANT范例的变量中,然后才作为参数传送出去。在VARIANT的vt成员的值假如包括VT_ARRAY│...,那么它所封装的就是一个SafeArray,它的parray成员等于指向SafeArray的指针。SafeArray中元素的范例可所以VARIANT能封装的任何范例,包罗VARIANT范例自己。 行使SafeArray的详细步调: 要领一: 包装一个SafeArray: (1). 界说变量,如: VARIANT varChunk; SAFEARRAY *psa; SAFEARRAYBOUND rgsabound[1]; (2). 建设SafeArray描写符: uIsRead=f.Read(bVal,ChunkSize);//read array from a file. if(uIsRead==0)break; rgsabound[0].cElements =uIsRead; rgsabound[0].lLbound = 0; psa = SafeArrayCreate(VT_UI1,1,rgsabound); (3). 安排数据元素到SafeArray: for(long index=0;index<uIsRead;index++) { if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index]))) ::MessageBox(NULL,"出短处了。","提醒",MB_OK │ MB_ICONWARNING); } 一个一个地放,挺贫困的。 (4). 封装到VARIANT内: varChunk.vt = VT_ARRAY│VT_UI1; varChunk.parray = psa; 这样就可以将varChunk作为参数传送出去了。 读取SafeArray中的数据的步调: (1). 用SafeArrayGetElement一个一个地读 BYTE buf[lIsRead]; for(long index=0;index<lIsRead;index++) { ::SafeArrayGetElement(varChunk.parray,&index,buf+index); } 就读到缓冲区buf里了。 要领二: 行使SafeArrayAccessData直接读写SafeArray的缓冲区: (1). 读缓冲区: BYTE *buf; SafeArrayAccessData(varChunk.parray, (void **)&buf); f.Write(buf,lIsRead); SafeArrayUnaccessData(varChunk.parray); (2). 写缓冲区: BYTE *buf; ::SafeArrayAccessData(psa, (void **)&buf); for(long index=0;index<uIsRead;index++) { buf[index]=bVal[index]; } ::SafeArrayUnaccessData(psa); varChunk.vt = VT_ARRAY│VT_UI1; varChunk.parray = psa; 这种要领读写SafeArray都可以,它直接哄骗SafeArray的数据缓冲区,比用SafeArrayGetElement和SafeArrayPutElement速率快。出格得当于读取数据。但用完之后不要忘了挪用::SafeArrayUnaccessData(psa),不然会堕落的。 13. 行使书签( bookmark ) 书签可以独一标识记录齐集的一个记录,用于快速地将当前记录移回到已会见过的记录,以及举办过滤等等。Provider会自动为记录齐集的每一笔记录发生一个书签,我们只必要行使它就行了。我们不能试图表现、修改或较量书签。ADO用记录集的Bookmark属性暗示当前记录的书签。 用法步调: (1). 成立一个VARIANT范例的变量 _variant_t VarBookmark; (2). 将当前记录的书签值存入该变量 也就是记录集的Bookmark属性的当前值。 VarBookmark = rst->Bookmark; (3). 返回到先前的记录 将生涯的书签值配置到记录集的书签属性中: // Check for whether bookmark set for a record if (VarBookmark.vt == VT_EMPTY) printf("No Bookmark set!n"); else rst->Bookmark = VarBookmark; 配置完后,当前记录即会移动到该书签指向的记录。 14、配置过滤前提 Recordset工具的Filter属性暗示了当前的过滤前提。它的值可所以以AND或OR毗连起来的前提表达式(不含WHERE要害字)、由书签构成的数组或ADO提供的FilterGroupEnum列举值。为Filter属性配置新值后Recordset的当前记录指针会自动移动到满意过滤前提的第一个记录。譬喻: rst->Filter = _bstr_t ("姓名='赵薇' AND 性别=’女’"); 在行使前提表达式时应留意下列题目: (1)、可以用圆括号构成伟大的表达式 譬喻: rst->Filter = _bstr_t ("(姓名='赵薇' AND 性别=’女’) OR AGE<25"); 可是微软不应承在括号内用OR,然后在括号外用AND,譬喻: rst->Filter = _bstr_t ("(姓名='赵薇' OR 性别=’女’) AND AGE<25"); 必需修改为: rst->Filter = _bstr_t ("(姓名='赵薇' AND AGE<25) OR (性别=’女’ AND AGE<25)"); (2)、表达式中的较量运算符可所以LIKE LIKE后被较量的是一个含有通配符*的字符串,星号暗示多少个恣意的字符。 字符串的首部和尾部可以同时带星号* rst->Filter = _bstr_t ("姓名 LIKE '*赵*' "); 也可以只是尾部带星号: rst->Filter = _bstr_t ("姓名 LIKE '赵*' "); Filter属性值的范例是Variant,假如过滤前提是由书签构成的数组,则需将该数组转换为SafeArray,然后再封装到一个VARIANT或_variant_t型的变量中,再赋给Filter属性。 15、索引与排序 (1)、成立索引 当以某个字段为要害字用Find要领查找时,为了加速速率可以以该字段为要害字在记录集内部姑且成立索引。只要将该字段的Optimize属性配置为true即可,譬喻: pRst->Fields->GetItem("姓名")->Properties-> GetItem("Optimize")->PutValue("True"); pRst->Find("姓名 = '赵薇'",1,adSearchForward); ...... pRst->Fields->GetItem("姓名")->Properties-> GetItem("Optimize")->PutValue("False"); pRst->Close(); 声名:Optimize属性是由Provider提供的属性(在ADO中称为动态属性),ADO自己没有此属性。 (2)、排序 要排序也很简朴,只要把要排序的要害字列表配置到Recordset工具的Sort属性里即可,譬喻: pRstAuthors->CursorLocation = adUseClient; pRstAuthors->Open("SELECT * FROM mytable", _variant_t((IDispatch *) pConnection), adOpenStatic, adLockReadOnly, adCmdText); ...... pRst->Sort = "姓名 DESC, 年数 ASC"; 要害字(即字段名)之间用逗号离隔,假如要以某要害字降序排序,则应在该要害字后加一空格,再加DESC(如上例)。升序时ASC加不加无所谓。本操纵是操作索引举办的,并未举办物理排序,以是服从较高。 但要留意,在打开记录集之前必需将记录集的CursorLocation属性配置为adUseClient,如上例所示。Sort属性值在必要时随时可以修改。 16、事宜处理赏罚 ADO中的事宜处理赏罚也很简朴,只需别离在恰当的位置挪用Connection工具的三个要领即可,这三个要领是: (1)、在事宜开始时挪用 pCnn->BeginTrans(); (2)、在事宜竣事并乐成时挪用 pCnn->CommitTrans (); (3)、在事宜竣事并失败时挪用 pCnn->RollbackTrans (); 在行使事宜处理赏罚时,应只管减小事宜的范畴,即减小从事宜开始到竣事(提交或回滚)之间的时距离断,以便进步体系服从。必要时也可在挪用BeginTrans()要领之前,先配置Connection工具的IsolationLevel属性值,具体内容拜见MSDN中有关ADO的技能资料。 三、行使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。 (编辑:湖南网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
站长推荐
热点阅读