国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > 綜合 > 正文

Visual C++ ADO數據庫編程入門(下)

2024-07-21 02:05:49
字體:
來源:轉載
供稿:網友
 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: %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 ();
  在使用事務處理時,應盡量減小事務的范圍,即減小從事務開始到結束(提交或回滾)之間的時間間隔,以便提高系統效率。需要時也可在調用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。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 广汉市| 霍山县| 樟树市| 平顶山市| 晋宁县| 涟水县| 九龙城区| 攀枝花市| 泰兴市| 波密县| 铜鼓县| 龙岩市| 雅江县| 陇南市| 阿拉善右旗| 石狮市| 比如县| 邛崃市| 晋州市| 定日县| 华宁县| 聊城市| 忻州市| 桂平市| 达尔| 松阳县| 讷河市| 阿拉尔市| 那坡县| 甘谷县| 五常市| 和林格尔县| 安岳县| 兰州市| 汶川县| 宁乡县| 全椒县| 泸定县| 神木县| 和林格尔县| 鄂托克前旗|