打印本文 打印本文 关闭窗口 关闭窗口
ADO数据库编程入门
作者:武汉SEO闵涛  文章来源:敏韬网  点击数2859  更新时间:2009/4/23 10:48:18  文章录入:mintao  责任编辑:mintao
nbsp;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: %s\t %s\tPhone: %s\n", 
  (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 ();
在使用事务处理时,应尽量减小事务的范围,即减小从事务开始到结束(提交或回滚)之间的时间间隔,以便提高

上一页  [1] [2] [3] [4]  下一页

打印本文 打印本文 关闭窗口 关闭窗口