加入收藏 | 设为首页 | 会员中心 | 我要投稿 湖南网 (https://www.hunanwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 电商 > 正文

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。

(编辑:湖南网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读