VC访问数据库学习总结
VC連接數(shù)據(jù)庫方式
目前Windows系統(tǒng)上常見的數(shù)據(jù)庫接口包括:ODBC(開放數(shù)據(jù)庫互連):訪問數(shù)據(jù)庫得先配置數(shù)據(jù)源
MFC(Microsoft基礎(chǔ)類)ODBC類
DAO(數(shù)據(jù)訪問對象)
RDO(遠程數(shù)據(jù)對象)
OLE DB(對象鏈接嵌入數(shù)據(jù)庫)
ADO(ActiveX數(shù)據(jù)對象)
ODBC(Open Database Connectivity,開放數(shù)據(jù)庫連接)是由Microsoft定義的一種數(shù)據(jù)庫訪問標準,它提供了一種標準的數(shù)據(jù)庫訪問方法以訪問不同數(shù)據(jù)庫提供商的數(shù)據(jù)庫,其本質(zhì)上是一組數(shù)據(jù)庫訪問API.雖然數(shù)據(jù)庫訪問有多種方法,但ODBC以其編程相對簡單,在實際編程中被廣泛使用。 VC++中提供了一組封裝了ODBC API的MFC ODBC類,以減少程序代碼編寫量。在VC++中使用MFC ODBC類訪問數(shù)據(jù)庫時,一般都是先配置或選擇已有的數(shù)據(jù)源,再構(gòu)造CDatabase類對象,打開數(shù)據(jù)源,用該數(shù)據(jù)庫類對象和SQL(結(jié)構(gòu)化查詢語言)可以對庫進行訪問,再構(gòu)造CRecordset類或其繼承類對象,用數(shù)據(jù)集類對象和SQL可以實現(xiàn)對庫中的表的操作。在VC++中用MFC ODBC類操作SQL Server數(shù)據(jù)庫基本上也是這個思路。?
1.配置數(shù)據(jù)源?
在程序中根據(jù)用戶選擇動態(tài)配置數(shù)據(jù)源而不調(diào)用ODBC數(shù)據(jù)源管理器,對于應(yīng)用程序開發(fā)有時是十分必要的。畢竟ODBC數(shù)據(jù)源管理器對于對數(shù)據(jù)庫不熟悉的用戶顯的復(fù)雜了,且ODBC數(shù)據(jù)源管理器的界面風(fēng)格有可能與整個應(yīng)用程序的界面風(fēng)格不一致,對于嚴謹?shù)膽?yīng)用程序開發(fā)者來說,這些都是不能容忍的。?
配置SQL Server數(shù)據(jù)源時,必須有SQL Server服務(wù)器名和服務(wù)器中的目標數(shù)據(jù)庫名(否則打開該數(shù)據(jù)源時,就會彈出配置數(shù)據(jù)源對話框或失敗)。這與配置Access等數(shù)據(jù)庫的數(shù)據(jù)源時指定數(shù)據(jù)庫文件的路徑不同。?
可以通過ODBC API函數(shù)SQLBrowseConnect得到本地所有的SQL Server服務(wù)器、服務(wù)器中的庫、語言信息等。其使用方法如下所示:?
1得到服務(wù)器名?
SQLBrowseConnect(hdbc, "DRIVER={SQL Server};", SQL_NTS, BrowseResult, sizeof(BrowseResult), &BrowseResultLen);?
其中hdbc是用SQLAllocHandle函數(shù)得到的連結(jié)句柄,第二個參數(shù)是指定連結(jié)屬性的輸入字符串,第三個參數(shù)是輸入字符串的長度,第四個參數(shù)是輸出字符串的指針,我們要得到的信息就存于這個字符串中,第五個參數(shù)指定輸出字符串的長度,第六個參數(shù)是實際返回字符串的長度。BrowseResult所指向的函數(shù)返回的字符串中含有形如“SERVER:Server={Server_name1,Server_name2,…}”的字符串,其中Server_name1、Server_name2就是SQL Server服務(wù)器名。?
2得到庫名?
當用戶選定一個服務(wù)器之后,可以進一步利用SQLBrowseConnect函數(shù)得到服務(wù)器中存在的庫或語言信息(如果需要的話)。?
SQLBrowseConnect(hdbc,"SERVER=Server_name1;UID=sa;PWD=515578;",SQL_NTS, BrowseResult, sizeof(BrowseResult), &BrowseResultLen);?
其中UID和PWD是訪問該服務(wù)器的用戶名和密碼。這次BrowseResult所指向的函數(shù)返回的字符串中含有形如DATABASE:Database={master,model,…}和LANGUAGE:Language={Arabic, Brazilian, English, …}的字符串,其中master、model為服務(wù)器中的數(shù)據(jù)庫名。?
利用這些得到信息就可以配置SQL Server數(shù)據(jù)源了:?
SQLConfigDataSource(NULL,ODBC_ADD_DSN,"SQL Server","DSN=myDSN\0 SERVER=xhm\0DATABASE=pubs\0\0" ))?
作者有幸在網(wǎng)上找到Santosh Rao編寫的CSQLInfoEnumerator類,該類有 EnumerateSQLServers、EnumerateDatabase、EnumerateDatabaseLanguage三個成員函數(shù),其封裝了SQLBrowseConnect函數(shù)并進行相應(yīng)的字符處理,大大簡化了SQLBrowseConnect函數(shù)的使用。?
2.與數(shù)據(jù)源建立連接?
如果使用ODBC API函數(shù)進行連結(jié),函數(shù)SQLConnect、SQLDriverConnect和SQLBrowseConnect都可以實現(xiàn)。其中SQLConnect函數(shù)用于給定所有參數(shù)直接建立與數(shù)據(jù)源的連接;SQLDriverConnect用于給定了部分連接參數(shù),并彈出數(shù)據(jù)源瀏覽窗口與用戶交互,獲得足夠的參數(shù)后建立與數(shù)據(jù)源的連接;而SQLBrowseConnect是通過迭代獲取連結(jié)參數(shù)再進行連接,其使用如前所述。?
MFC為連接數(shù)據(jù)源提供了一個數(shù)據(jù)庫類CDatabase,通過它我們可以非常方便地與數(shù)據(jù)源建立連結(jié):?
//m_db是CDatabase的對象?
//m_szUserId,m_szPassword是CString對象,為訪問數(shù)據(jù)源的用戶名和密碼?
CString str;?
str = _T("DSN=xhmtest;UID=")+m_szUserId+_T(";PWD=")+m_szPassword;?
if (! m_db.OpenEx(str, CDatabase::noOdbcDialog ) ){?
AfxMessageBox("打開數(shù)據(jù)源失敗");?
}?
另外,如果我們想對數(shù)據(jù)源進行操作,就可以利用打開的CDatabase對象執(zhí)行SQL語句實現(xiàn)。如新建一張表:?
str = "CREATE TABLE TableDemo (Column1TEXT, Column2 NUMBER)";?
m_db.ExecuteSQL(sSql);?
3.得到數(shù)據(jù)庫的結(jié)構(gòu)信息?
在VC++中可以單獨利用ODBC API獲取數(shù)據(jù)庫的結(jié)構(gòu)信息,但方法非常復(fù)雜,各種參數(shù)也不易理解,且返回的結(jié)果不易獲取。MFC中也沒有為應(yīng)用程序開發(fā)者得到數(shù)據(jù)庫結(jié)構(gòu)信息而提供單獨的類。但是我們還是可以通過一些已有的方法方便地獲得數(shù)據(jù)庫的結(jié)構(gòu)信息。?
3.1 得到數(shù)據(jù)庫中的表?
利用ODBC API函數(shù)SQLTables可以得到數(shù)據(jù)庫中的表信息,但使用不易。?
在MSDN的sample中有一個catalog例程,它示范了如果得到一個數(shù)據(jù)庫的表信息和表中的字段信息,該程序中有兩個非常實用的類:Ctables和Ccolumns.其中CTables繼承CRecordset類,通過非常巧妙的方法封裝了SQLTables函數(shù),并將結(jié)果集存于CRecordset類,方便獲取。?
在程序中我們可以這樣應(yīng)用:?
//m_db是已經(jīng)打開的CDatabse對象,?
//m_strTableOwner是CComboBox對象?
CTables rs(&m_db);?
rs.Open(NULL, NULL, NULL, "TABLE");?
CString strTableRef;?
m_combTable.ResetContent();?
while (!rs.IsEOF())?
{?
strTableRef = _T("[");?
if (!rs.m_strTableOwner.IsEmpty())?
strTableRef += rs.m_strTableOwner + _T("].[");?
strTableRef += rs.m_strTableName + _T("]");?
m_combTable.AddString(strTableRef);?
rs.MoveNext();?
}?
rs.Close();?
在CTables對象Open方法第四個能數(shù)指定“TABLE”是指返回庫中的表,當該參數(shù)為“SYSTEM TABLE”時返回系統(tǒng)表,當為NULL時,返回所有表。?
3.2 得到表中字段結(jié)構(gòu)?
得到表中字段結(jié)構(gòu)有三種具體實現(xiàn)方式:?
1直接利用ODBC API 函數(shù)SQLColumn;其實現(xiàn)也同樣復(fù)雜。?
2利用前面提到的封裝了SQLColumns函數(shù)的CColumns類;其使用方法與CTables類似,其成員變量m_strColumnName即為字段名?
3利用CRecordset類的成員函數(shù)。MFC在CRecordset類中提供了獲取表結(jié)構(gòu)信息方法,使用非常方便。(但很奇怪為什么CDatabase類沒有獲取庫結(jié)構(gòu)信息的類方法?)?
我們以利用CRecodrset為例簡單說明怎樣得到表結(jié)構(gòu)。假設(shè)現(xiàn)在要得到用戶在CComboBox對象中所選表的所有字段名,并將它埴入一個CListCtrl對象(其View屬性被設(shè)為Report)的列名中去:?
//m_strTableOwner和上同,為CComboBox對象?
//m_listData為ClistCtrl對象?
CRecordset rs?
CODBCFieldInfo info;?
CString strSQL;?
m_combTable.GetLBText(m_combTable.GetCurSel(), strSQL);?
strSQL = _T("SELECT * FROM ") + strSQL;?
rs.Open(Open(CRecordset::snapshot, strSQL, CRecordset::readOnly);?
int nColumns = rs.GetODBCFieldCount();?
for (int nNum = 0; nNum < nColumns; nNum++)?
{?
prs->GetODBCFieldInfo(nNum, info);?
m_listData.InsertColumn(nNum, info.m_strName, LVCFMT_LEFT, 80)?
}?
另外,我們可以利用CRecordSet::GetFieldValue函數(shù)通過指定列名或列序數(shù)得到記錄集中游標所指記錄的值,通過CRecordset::AddNew、CRecordset::CancelUpdate、CRecordset::Delete、CRecordset::Edit、CRecordset::Update等函數(shù)操作CRecordset打開的表。
========
vc++與MySQL數(shù)據(jù)庫的連接
1.MySQL數(shù)據(jù)庫的安裝
?你可以從MySQL的官網(wǎng)上或者從如下地址下載MySQL的數(shù)據(jù)庫安裝包(http://download.csdn.net/detail/nuptboyzhb/4619847)。本文以mysql-5.0.27-win32為例。下載完之后解壓安裝。注意:在安裝的過程中,選擇安裝“完全版”(complete),不要選擇默認的“典型”。否者,沒有c++相關(guān)的連接庫。然后一直點next即可。安裝過程中讓你注冊,你就按部就班的做就行了。安裝完之后的文件目錄如下:
?[Image]
2.數(shù)據(jù)庫的建立
?你可以直接用MySQL的命令行窗口去建立一個數(shù)據(jù)庫和一個表。但是,我強烈推薦你用可視化的工具(你可以去搜一下)。在這里,我之前安裝過wamp5就直接用網(wǎng)頁版的phpmyadmin工具。如果你安裝過wamp5,直接在瀏覽器中輸入:http://localhost/phpmyadmin/即可。然后我們新建一個名為testdb的數(shù)據(jù)庫再在其中新建一個表:name_table.然后新增數(shù)據(jù):zhb 22studentsnjupt
?[Image][Image]
3.VC++6.0的配置
本文以經(jīng)典的vc++6.0為例。(當然,VS2005 2008 2010等,也是這樣配置)。打開vc++6.0,工具->選項->目錄(選項卡),在其Include files添加MySQL的include路徑。如我的MySQL的include文件夾的路徑為:C:\Program Files\MySQL\MySQL Server 5.0\include。切換下拉框,選擇Library files,添加MySQL的lib路徑。如我的為:C:\Program Files\MySQL\MySQL Server 5.0\lib\opt
4.編程連接數(shù)據(jù)庫并查詢
[c++ codes]
源碼打印?
#include <windows.h> ?
#include <stdio.h> ?
#include <stdlib.h> ?
#include <string.h> ?
#include <mysql.h> ??
#include <iostream> ?
#pragma comment(lib,"libmysql.lib")//連接MysQL需要的庫 ?
using namespace std; ?
int main() ?
{ ?
? ? const char user[] = "root"; ? ? ? ? //username ?
? ? const char pswd[] = "*********"; ? ?//password ?
? ? const char host[] = "localhost"; ? ?//or"127.0.0.1" ?
? ? const char table[] ="testdb"; ? ? ? //database ?
? ? unsigned int port = 3306; ? ? ? ? ? //server port ? ? ? ? ?
? ? MYSQL myCont; ?
? ? MYSQL_RES *result; ?
? ? MYSQL_ROW sql_row; ?
? ? MYSQL_FIELD *fd; ?
? ? char column[32][32]; ?
? ? int res; ?
? ? mysql_init(&myCont); ?
? ? if(mysql_real_connect(&myCont,host,user,pswd,table,port,NULL,0)) ?
? ? { ?
? ? ? ? cout<<"connect succeed!"<<endl; ?
? ? ? ? mysql_query(&myCont, "SET NAMES GBK"); //設(shè)置編碼格式,否則在cmd下無法顯示中文 ?
? ? ? ? res=mysql_query(&myCont,"select * from name_table");//查詢 ?
? ? ? ? if(!res) ?
? ? ? ? { ?
? ? ? ? ? ? result=mysql_store_result(&myCont);//保存查詢到的數(shù)據(jù)到result ?
? ? ? ? ? ? if(result) ?
? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? int i,j; ?
? ? ? ? ? ? ? ? cout<<"number of result: "<<(unsigned long)mysql_num_rows(result)<<endl; ?
? ? ? ? ? ? ? ? for(i=0;fd=mysql_fetch_field(result);i++)//獲取列名 ?
? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? strcpy(column[i],fd->name); ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? j=mysql_num_fields(result); ?
? ? ? ? ? ? ? ? for(i=0;i<j;i++) ?
? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? printf("%s\t",column[i]); ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? printf("\n"); ?
? ? ? ? ? ? ? ? while(sql_row=mysql_fetch_row(result))//獲取具體的數(shù)據(jù) ?
? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? for(i=0;i<j;i++) ?
? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? printf("%s\n",sql_row[i]); ?
? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? ? ? printf("\n"); ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? } ?
? ? ? ? } ?
? ? ? ? else ?
? ? ? ? { ?
? ? ? ? ? ? cout<<"query sql failed!"<<endl; ?
? ? ? ? } ?
? ? } ?
? ? else ?
? ? { ?
? ? ? ? cout<<"connect failed!"<<endl; ?
? ? } ?
? ? if(result!=NULL) mysql_free_result(result);//釋放結(jié)果資源 ?
? ? mysql_close(&myCont);//斷開連接 ?
? ? return 0; ?
} ?
實驗結(jié)果
[Image]
http://blog.csdn.net/nuptboyzhb/article/details/8043091
========
?用vc如何訪問ACCESS數(shù)據(jù)庫?
? ? ? 在現(xiàn)代軟件開發(fā)中,數(shù)據(jù)庫技術(shù)被越來越廣泛應(yīng)用,很多項目都存在著大量的數(shù)據(jù)需要存儲,通常都會采用數(shù)據(jù)庫來存儲這些數(shù)據(jù)。最初,數(shù)據(jù)庫廠商推出一個新的數(shù)據(jù)庫產(chǎn)品時,相應(yīng)的,他會為程序員提供一套訪問該數(shù)據(jù)庫的接口,即API。不同的數(shù)據(jù)庫廠商提供的訪問接口是不一樣的,這樣在使用一個新的數(shù)據(jù)庫時,就要學(xué)習(xí)一套新的API,當然這就加大了開發(fā)數(shù)據(jù)庫的難度,也不利于數(shù)據(jù)庫在軟件開發(fā)過程中的應(yīng)用。因此,后來Microsoft就推出了一些標準的訪問數(shù)據(jù)庫的技術(shù)。
? ? ? 這一部分主要介紹ADO訪問數(shù)據(jù)庫的情況,對于其它技術(shù),可以自行查閱相應(yīng)書籍,ADO中有以下3個核心對象。
@Connection對象
? ? ? Connection對象表示到數(shù)據(jù)庫的鏈接,它管理應(yīng)用程序和數(shù)據(jù)庫之間的通信,下面的2個對象都有一個ActiveConnection屬性,該屬性用來引用Connection對象。
@Command對象
? ? ? Command對象用來 處理 重復(fù)執(zhí)行的查詢,或處理 需要檢查 在存儲過程中的輸出或返回值 的查詢。
@RecordSet對象
? ? ? RecordSet對象用來保存數(shù)據(jù),存放查詢的結(jié)果,這些結(jié)果由數(shù)據(jù)的行(記錄)和列(字段)組成。每一列都存放在RecordSet的Fields集合中的一個Field對象中。
第一步:
? ? ? 在VC中利用ADO訪問數(shù)據(jù)庫時,首先需要導(dǎo)入ADO庫。在程序的預(yù)編譯頭文件stdAfx.h中導(dǎo)入該庫,方法是利用import指令將此動態(tài)鏈接庫導(dǎo)入,具體代碼如下:#import "c:/program files/common files/system/ado/msado15.dll" no_namespace rename("EOF","rsEOF"),代碼中使用了no_namespace關(guān)鍵字,namespace是命名空間,本例中不需要命名空間,主要是為了訪問方便,在程序中可以直接訪問ADO提供的Connection、Command、RecordSet這3個COM接口。最后利用rename將EOF改為rsEOF。EOF表示 記錄集 的結(jié)尾,因為文件也是以EOF為結(jié)尾的,為了避免沖突,在導(dǎo)入ADO庫時,需要將EOF改為rsEOF。至于將其改為什么名字,可以根據(jù)自己的習(xí)慣來定。
? ? ? 在導(dǎo)入ADO庫之后,編譯程序,會出現(xiàn)一條警告,在VC中利用ADO訪問數(shù)據(jù)庫時都會存在這一警告,對程序并沒有影響,可以不用理會。編譯之后,在Debug目錄下,可以看到編譯器為我們產(chǎn)生了兩個文件,msado15.tlh和msado15.tli。可以將msado15.tlh看作是一個頭文件,msado15.tli看作是一個源文件,這兩個文件是在導(dǎo)入ADO庫之后,編譯器在編譯的時候自動生成的,利用ADO技術(shù)訪問數(shù)據(jù)庫并不需要為工程導(dǎo)入這兩個文件。
第二步:
? ? ? 為需要訪問數(shù)據(jù)庫的類添加2個成員變量,_RecordsetPtr m_pRs(數(shù)據(jù)集對象);_ConnectionPtr m_pConn;(數(shù)據(jù)庫鏈接對象) ? 接著在程序初始化的時候初始化這2個對象
CoInitialize(NULL); ?
? ? try ?
? ? { ?
? ? ? ? //初始化數(shù)據(jù)庫連接對象 ?
? ? ? ? m_pConn.CreateInstance("ADODB.Connection"); ?
? ? ? ? m_pRs.CreateInstance(__uuidof(Recordset));//這里必須初始化。 ?
? ? ? ? //定義數(shù)據(jù)庫連接字符串,打開本地Access庫 ?
? ? ? ? m_pConn->ConnectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=DBS.mdb;Persist Security Info=False"; ?
? ? ? ? //打開數(shù)據(jù)庫連接 ?
? ? ? ? m_pConn->Open("","","",adConnectUnspecified); ?
? ? ? ? ??
? ? } ?
? ? catch(_com_error &e) ?
? ? { ?
? ? ? ? ::CoUninitialize(); ?
? ? ? ? ::AfxMessageBox(e.ErrorMessage()); ?
? ? ? ? return FALSE; ?
? ? ? ? ??
? ? } ?
?
COM組件在使用時,需要初始化COM庫,這就需要調(diào)用CoInitialize(NULL);函數(shù)來實現(xiàn),該函數(shù)有一個參數(shù),該參數(shù)是保留的,直接傳遞NULL即可,同時在訪問完COM庫之后,程序需要調(diào)用CoUninitialize();函數(shù)卸載COM庫。
? ? ? 接著,初始化 數(shù)據(jù)鏈接對象 和 數(shù)據(jù)集對象。在初始化這兩個對象時,利用__uuidof獲取ADO Connection和ADO RecordSet對象的全局唯一標識符(GUID)。對m_pConn對象進行初始化。
? ? ? 然后,利用智能指針對象去訪問該對象的屬性和方法了。首先,為數(shù)據(jù)庫鏈接對象的鏈接字符串賦值。然后調(diào)用鏈接對象的Open方法打開與數(shù)據(jù)庫的鏈接。關(guān)于鏈接字符串的獲得:不必死記硬背,可以利用VB開發(fā)環(huán)境來得到鏈接字符串,P756。
? ? ? 接下來就可以利用鏈接對象的Execute方法來從數(shù)據(jù)庫獲得數(shù)據(jù)了。
void CMy_DbsDlg::OnBtnQue() ??
{ ? ??
? ? try ?
? ? { ?
? ? ? ? CString strsql; ?
? ? ? ? strsql.Format("SELECT * FROM RoomStandard"); ?
? ? ? ? HRESULT hr=m_pRs->Open(_variant_t(strsql),(m_pConn.GetInterfacePtr()),adOpenStatic,adLockOptimistic,adCmdText); ?
? ? ? ? //m_pRs=m_pConn->Execute((_bstr_t)strsql,NULL,adCmdText); ?
? ? ? ? _variant_t vFieldValue; ?
? ? ? ? if(SUCCEEDED(hr)) ?
? ? ? ? { ?
? ? ? ? ? ? //m_pRs->MoveFirst();//移至第一條記錄---------默認指針在第一條記錄上 ?
? ? ? ? ? ? m_list.DeleteAllItems(); ?
? ? ? ? ? ? int i=0,n=0; ?
? ? ? ? ? ? while(!(m_pRs->GetrsEOF()))//while(!m_pRs->rsEOF) ?
? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? int k=0; ?
? ? ? ? ? ? ? ? vFieldValue=m_pRs->GetCollect(_variant_t(long(k))); ?
? ? ? ? ? ? ? ? if(vFieldValue.vt!=NULL) ?
? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? k=1; ?
? ? ? ? ? ? ? ? ? ? m_list.InsertItem(n,_bstr_t(vFieldValue));//插入列表控件 ?
? ? ? ? ? ? ? ? ? ? for(;k<9;k++) ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? vFieldValue=m_pRs->GetCollect(_variant_t(long(k))); ?
? ? ? ? ? ? ? ? ? ? ? ? if(vFieldValue.vt!=NULL) ?
? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? m_list.SetItemText(n,k,_bstr_t(vFieldValue)); ?
? ? ? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? n++;//記錄數(shù)加1; ?
? ? ? ? ? ? ? ? m_pRs->MoveNext(); ?
? ? ? ? ? ? } ?
? ? ? ? ? ? m_pRs->Close(); ?
? ? ? ? } ?
? ? } ?
? ? catch(_com_error e) ?
? ? { ?
? ? ? ? AfxMessageBox(e.ErrorMessage()); ?
? ? ? ? AfxMessageBox((LPCTSTR)(e.Description())); ?
? ? } ?
? ? /// ?
? ? //這里要判斷一下記錄是不是打開的,要以防不正常的情況下記錄沒有關(guān)閉! ?
? ? if(m_pRs) ?
? ? { ?
? ? ? ? if (m_pRs->State) ?
? ? ? ? { ? ? ? ??
? ? ? ? ? ? m_pRs->Close(); ?
? ? ? ? } ?
? ? } ?
} ?
?
? ? ? 當數(shù)據(jù)操作完成之后,調(diào)用記錄集對象的Close方法關(guān)閉記錄集,最后調(diào)用鏈接對象的Close方法關(guān)閉鏈接。然后還需要釋放智能指針在COM接口上的引用計數(shù),即分別調(diào)用記錄集對象和鏈接對象的Release方法,該方法將釋放相應(yīng)COM接口的引用計數(shù)。智能指針有一個特點,在訪問其它屬性和方法時,都是用->操作符,但在調(diào)用Release方法釋放引用計數(shù)時,必須使用.操作符。
========
VC++ADO連接數(shù)據(jù)庫 ??
一、ADO簡介 ?
ADO(ActiveX Data Object)是Microsoft數(shù)據(jù)庫應(yīng)用程序開發(fā)的新接口,是建立在OLE DB之上的高層數(shù)據(jù)?
庫訪問技術(shù),請不必為此擔(dān)心,即使你對OLE DB,COM不了解也能輕松對付ADO,因為它非常簡單易用,甚?
至比你以往所接觸的ODBC API、DAO、RDO都要容易使用,并不失靈活性。本文將詳細地介紹在VC下如何使?
用ADO來進行數(shù)據(jù)庫應(yīng)用程序開發(fā),并給出示例代碼。 ?
本文示例代碼 ?
二、基本流程 ?
?
(1)初始化COM庫,引入ADO庫定義文件 ?
(2)用Connection對象連接數(shù)據(jù)庫 ?
(3)利用建立好的連接,通過Connection、Command對象執(zhí)行SQL命令,或利用Recordset對象取得結(jié)果記錄?
集進行查詢、處理。 ?
(4)使用完畢后關(guān)閉連接釋放對象。 ?
準備工作: ?
為了大家都能測試本文提供的例子,我們采用Access數(shù)據(jù)庫,您也可以直接在我們提供的示例代碼中找到?
這個test.mdb。 ?
下面我們將詳細介紹上述步驟并給出相關(guān)代碼。 ?
【1】COM庫的初始化 ?
我們可以使用AfxOleInit()來初始化COM庫,這項工作通常在CWinApp::InitInstance()的重載函數(shù)中完成?
,請看如下代碼: ?
BOOL CADOTest1App::InitInstance() ?
? { ?
? AfxOleInit(); ?
? ...... ?
【2】用#import指令引入ADO類型庫 ?
我們在stdafx.h中加入如下語句:(stdafx.h這個文件哪里可以找到?你可以在FileView中的Header Files?
里找到) ?
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename?
("EOF","adoEOF") ?
這一語句有何作用呢?其最終作用同我們熟悉的#i nclude類似,編譯的時候系統(tǒng)會為我們生成?
msado15.tlh,ado15.tli兩個C++頭文件來定義ADO庫。 ?
幾點說明: ?
(1) 您的環(huán)境中msado15.dll不一定在這個目錄下,請按實際情況修改 ?
(2) 在編譯的時候肯能會出現(xiàn)如下警告,對此微軟在MSDN中作了說明,并建議我們不要理會這個警告。 ?
msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result ?
still unsigned ?
【3】創(chuàng)建Connection對象并連接數(shù)據(jù)庫 ?
首先我們需要添加一個指向Connection對象的指針: ?
_ConnectionPtr m_pConnection; ?
下面的代碼演示了如何創(chuàng)建Connection對象實例及如何連接數(shù)據(jù)庫并進行異常捕捉。 ?
BOOL CADOTest1Dlg::OnInitDialog() ?
? { ?
? CDialog::OnInitDialog(); ?
? HRESULT hr; ?
? try ?
? { ?
? hr = m_pConnection.CreateInstance("ADODB.Connection");///創(chuàng)建Connection對象 ?
? if(SUCCEEDED(hr)) ?
? { ?
? hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data ?
Source=test.mdb","","",adModeUnknown);///連接數(shù)據(jù)庫 ?
? ///上面一句中連接字串中的Provider是針對ACCESS2000環(huán)境的,對于ACCESS97,需要改?
為:Provider=Microsoft.Jet.OLEDB.3.51; ?} ?
? } ?
? catch(_com_error e)///捕捉異常 ?
? { ?
? CString errormessage; ?
? errormessage.Format("連接數(shù)據(jù)庫失敗!\r\n錯誤信息:%s",e.ErrorMessage()); ?
? AfxMessageBox(errormessage);///顯示錯誤信息 ?
? } ?
在這段代碼中我們是通過Connection對象的Open方法來進行連接數(shù)據(jù)庫的,下面是該方法的原型 ?
HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, ?
long Options ) ?
ConnectionString為連接字串,UserID是用戶名, Password是登陸密碼,Options是連接選項,用于指定?
Connection對象對數(shù)據(jù)的更新許可權(quán), ?
Options可以是如下幾個常量: ?
adModeUnknown:缺省。當前的許可權(quán)未設(shè)置 ?
adModeRead:只讀 ?
adModeWrite:只寫 ?
adModeReadWrite:可以讀寫 ?
adModeShareDenyRead:阻止其它Connection對象以讀權(quán)限打開連接 ?
adModeShareDenyWrite:阻止其它Connection對象以寫權(quán)限打開連接 ?
adModeShareExclusive:阻止其它Connection對象打開連接 ?
adModeShareDenyNone:允許其它程序或?qū)ο笠匀魏螜?quán)限建立連接 ?
我們給出一些常用的連接方式供大家參考: ?
(1)通過JET數(shù)據(jù)庫引擎對ACCESS2000數(shù)據(jù)庫的連接 ?
m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data ?
Source=C:\\test.mdb","","",adModeUnknown); ?
(2)通過DSN數(shù)據(jù)源對任何支持ODBC的數(shù)據(jù)庫進行連接: ?
m_pConnection->Open("Data Source=adotest;UID=sa;PWD=;","","",adModeUnknown); ?
(3)不通過DSN對SQL SERVER數(shù)據(jù)庫進行連接: m_pConnection->Open("driver={SQL ?
Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown); ?
其中Server是SQL服務(wù)器的名稱,DATABASE是庫的名稱 ?
Connection對象除Open方法外還有許多方法,我們先介紹Connection對象中兩個有用的屬性?
ConnectionTimeOut與State ?
ConnectionTimeOut用來設(shè)置連接的超時時間,需要在Open之前調(diào)用,例如: m_pConnection-?
>ConnectionTimeout = 5;///設(shè)置超時時間為5秒 ?
m_pConnection->Open("Data Source=adotest;","","",adModeUnknown); ?
State屬性指明當前Connection對象的狀態(tài),0表示關(guān)閉,1表示已經(jīng)打開,我們可以通過讀取這個屬性來?
作相應(yīng)的處理,例如: ?
if(m_pConnection->State) ?
? ? ?m_pConnection->Close(); ///如果已經(jīng)打開了連接則關(guān)閉它 ?
【4】執(zhí)行SQL命令并取得結(jié)果記錄集 ?
為了取得結(jié)果記錄集,我們定義一個指向Recordset對象的指針:_RecordsetPtr m_pRecordset; ?
并為其創(chuàng)建Recordset對象的實例: m_pRecordset.CreateInstance("ADODB.Recordset"); ?
SQL命令的執(zhí)行可以采用多種形式,下面我們一進行闡述。 ?
(1)利用Connection對象的Execute方法執(zhí)行SQL命令 ?
Execute方法的原型如下所示: ?
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long ?
Options ) 其中CommandText是命令字串,通常是SQL命令。參數(shù)RecordsAffected是操作完成后所影響的?
行數(shù), 參數(shù)Options表示CommandText中內(nèi)容的類型,Options可以取如下值之一: ?
adCmdText:表明CommandText是文本命令 ?
adCmdTable:表明CommandText是一個表名 ?
adCmdProc:表明CommandText是一個存儲過程 ?
adCmdUnknown:未知 ?
Execute執(zhí)行完后返回一個指向記錄集的指針,下面我們給出具體代碼并作說明。 ? _variant_t ?
RecordsAffected; ?
? ///執(zhí)行SQL命令:CREATE TABLE創(chuàng)建表格users,users包含四個字段:整形ID,字符串username,整形old,?
日期型birthday ?
? m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday ?
DATETIME)",&RecordsAffected,adCmdText); ?
? ///往表格里面添加記錄 ?
? m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) valueS (1, ?
nullnullnullnullnullnullnullnullWashingtonnullnullnullnullnullnullnullnull,25,nullnullnullnu?
llnullnullnullnull1970/1/1nullnullnullnullnullnullnullnull)",&RecordsAffected,adCmdText); ?
? ///將所有記錄old字段的值加一 ?
? m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText); ?
? ///執(zhí)行SQL統(tǒng)計命令得到包含記錄條數(shù)的記錄集 ?
? m_pRecordset = ?m_pConnection->Execute("SELECT COUNT(*) FROM ?
users",&RecordsAffected,adCmdText); ?
? _variant_t vIndex = (long)0; ?
? _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一個字段的值放入vCount變量 ?
? m_pRecordset->Close();///關(guān)閉記錄集 ?
? CString message; ?
? message.Format("共有%d條記錄",vCount.lVal); ?
? AfxMessageBox(message);///顯示當前記錄條數(shù) ?
(2)利用Command對象來執(zhí)行SQL命令 ?
? _CommandPtr m_pCommand; ?
? m_pCommand.CreateInstance("ADODB.Command"); ?
? _variant_t vNULL; ?
? vNULL.vt = VT_ERROR; ?
? vNULL.scode = DISP_E_PARAMNOTFOUND;///定義為無參數(shù) ?
? m_pCommand->ActiveConnection = m_pConnection;///非常關(guān)鍵的一句,將建立的連接賦值給它 ?
? m_pCommand->CommandText = "SELECT * FROM users";///命令字串 ?
? m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///執(zhí)行命令,取得記錄集 ?
在這段代碼中我們只是用Command對象來執(zhí)行了SELECT查詢語句,Command對象在進行存儲過程的調(diào)用中能?
真正體現(xiàn)它的作用。下次我們將詳細介紹。 ?
(3)直接用Recordset對象進行查詢?nèi)〉糜涗浖??
例如 ?
? m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch *)?
m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); ?
Open方法的原型是這樣的: ?
HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, ?
enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options ) ?
其中: ?
1Source是數(shù)據(jù)查詢字符串 ?
2ActiveConnection是已經(jīng)建立好的連接(我們需要用Connection對象指針來構(gòu)造一個_variant_t對象) ?
3CursorType光標類型,它可以是以下值之一,請看這個枚舉結(jié)構(gòu): ?
enum CursorTypeEnum ?
{ ?
adOpenUnspecified = -1,///不作特別指定 ?
adOpenForwardOnly = 0,///前滾靜態(tài)光標。這種光標只能向前瀏覽記錄集,比如用MoveNext向前滾動,這?
種方式可以提高瀏覽速度。但諸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用 ?
adOpenKeyset = 1,///采用這種光標的記錄集看不到其它用戶的新增、刪除操作,但對于更新原有記錄的?
操作對你是可見的。 ?
adOpenDynamic = 2,///動態(tài)光標。所有數(shù)據(jù)庫的操作都會立即在各用戶記錄集上反應(yīng)出來。 ?
adOpenStatic = 3///靜態(tài)光標。它為你的記錄集產(chǎn)生一個靜態(tài)備份,但其它用戶的新增、刪除、更新操?
作對你的記錄集來說是不可見的。 ?
}; ?
4LockType鎖定類型,它可以是以下值之一,請看如下枚舉結(jié)構(gòu): ?
enum LockTypeEnum ?
{ ?
adLockUnspecified = -1,///未指定 ?
adLockReadOnly = 1,///只讀記錄集 ?
adLockPessimistic = 2,悲觀鎖定方式。數(shù)據(jù)在更新時鎖定其它所有動作,這是最安全的鎖定機制 ?
adLockOptimistic = 3,樂觀鎖定方式。只有在你調(diào)用Update方法時才鎖定記錄。在此之前仍然可以做數(shù)?
據(jù)的更新、插入、刪除等動作 ?
adLockBatchOptimistic = 4,樂觀分批更新。編輯時記錄不會鎖定,更改、插入及刪除是在批處理模式?
下完成。 ?
}; ?
5Options請參考本文中對Connection對象的Execute方法的介紹 ?
【5】記錄集的遍歷、更新 ?
根據(jù)我們剛才通過執(zhí)行SQL命令建立好的users表,它包含四個字段:ID,username,old,birthday ?
以下的代碼實現(xiàn):打開記錄集,遍歷所有記錄,刪除第一條記錄,添加三條記錄,移動光標到第二條記錄?
,更改其年齡,保存到數(shù)據(jù)庫。 ?
_variant_t vUsername,vBirthday,vID,vOld; ?
_RecordsetPtr m_pRecordset; ?
m_pRecordset.CreateInstance("ADODB.Recordset"); ?
m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch*)?
m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); ?
while(!m_pRecordset->adoEOF)///這里為什么是adoEOF而不是EOF呢?還記得rename("EOF","adoEOF")這?
一句嗎? ?
{ ?
vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,從0開始計數(shù),你也可以直?
接給出列的名稱,如下一行 ?
vUsername = m_pRecordset->GetCollect("username");///取得username字段的值 ?
vOld = m_pRecordset->GetCollect("old"); ?
vBirthday = m_pRecordset->GetCollect("birthday"); ?
///在DEBUG方式下的OUTPUT窗口輸出記錄集中的記錄 ?
if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != ?
VT_NULL) ?
? TRACE("id:%d,姓名:%s,年齡:%d,生日:%s\r\n",vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,?
(LPCTSTR)(_bstr_t)vBirthday); ?
m_pRecordset->MoveNext();///移到下一條記錄 ?
} ?
m_pRecordset->MoveFirst();///移到首條記錄 ?
m_pRecordset->Delete(adAffectCurrent);///刪除當前記錄 ?
///添加三條新記錄并賦值 ?
for(int i=0;i<3;i++) ?
{ ?
m_pRecordset->AddNew();///添加新記錄 ?
m_pRecordset->PutCollect("ID",_variant_t((long)(i+10))); ?
m_pRecordset->PutCollect("username",_variant_t("葉利欽")); ?
m_pRecordset->PutCollect("old",_variant_t((long)71)); ?
m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15")); ?
} ?
m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///從第一條記錄往下移動一條記錄,即移?
動到第二條記錄處 ?
m_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年齡 ?
m_pRecordset->Update();///保存到庫中 ?
【6】關(guān)閉記錄集與連接 ?
記錄集或連接都可以用Close方法來關(guān)閉 ?
? ? ? m_pRecordset->Close();///關(guān)閉記錄集 ?
? ? ? m_pConnection->Close();///關(guān)閉連接 ?
,最后我給大家寫了一個小例子,例子中讀出所有記錄放?
到列表控件中、并可以添加、刪除、修改記錄。 ?
點這里下載示例代碼 ?
后記:限于篇幅ADO中的許多內(nèi)容還沒有介紹,下次我們將詳細介紹Recordset對象的屬性、方法并解決幾?
個關(guān)鍵的技術(shù):綁定方式處理記錄集數(shù)據(jù)、存儲過程的調(diào)用、事務(wù)處理、圖象在數(shù)據(jù)庫中的保存與讀取、?
與表格控件的配合使用等。?
========
VC對ADO的操作
文章概要:VC用ADO訪問數(shù)據(jù)庫全攻略,介紹了VC用ADO來訪問數(shù)據(jù)庫的各個對象及各方法,
一、ADO概述?
ADO是Microsoft為最新和最強大的數(shù)據(jù)訪問范例 OLE DB 而設(shè)計的,是一個便于使用的應(yīng)用程序?qū)咏涌凇DO 使您能夠編寫應(yīng)用程序以通過 OLE. DB 提供者訪問和操作數(shù)據(jù)庫服務(wù)器中的數(shù)據(jù)。ADO 最主要的優(yōu)點是易于使用、速度快、內(nèi)存支出少和磁盤遺跡小。ADO 在關(guān)鍵的應(yīng)用方案中使用最少的網(wǎng)絡(luò)流量,并且在前端和數(shù)據(jù)源之間使用最少的層數(shù),所有這些都是為了提供輕量、高性能的接口。之所以稱為 ADO,是用了一個比較熟悉的暗喻,OLE 自動化接口。
OLE DB是一組”組件對象模型”(COM) 接口,是新的數(shù)據(jù)庫低層接口,它封裝了ODBC的功能,并以統(tǒng)一的方式訪問存儲在不同信息源中的數(shù)據(jù)。OLE DB是Microsoft UDA(Universal Data Access)策略的技術(shù)基礎(chǔ)。OLE DB 為任何數(shù)據(jù)源提供了高性能的訪問,這些數(shù)據(jù)源包括關(guān)系和非關(guān)系數(shù)據(jù)庫、電子郵件和文件系統(tǒng)、文本和圖形、自定義業(yè)務(wù)對象等等。也就是說,OLE DB 并不局限于 ISAM、Jet 甚至關(guān)系數(shù)據(jù)源,它能夠處理任何類型的數(shù)據(jù),而不考慮它們的格式和存儲方法。在實際應(yīng)用中,這種多樣性意味著可以訪問駐留在 Excel 電子數(shù)據(jù)表、文本文件、電子郵件/目錄服務(wù)甚至郵件服務(wù)器,諸如 Microsoft Exchange 中的數(shù)據(jù)。但是,OLE DB 應(yīng)用程序編程接口的目的是為各種應(yīng)用程序提供最佳的功能,它并不符合簡單化的要求。您需要的API 應(yīng)該是一座連接應(yīng)用程序和OLE DB 的橋梁,這就是 ActiveX Data Objects (ADO)。
二、在VC中使用ADO(開發(fā)步驟好下:)
1、引入ADO庫文件
使用ADO前必須在工程的stdafx.h頭文件里用直接引入符號#import引入ADO庫文件,以使編譯器能正確編譯。代碼如下所示:
用#import引入ADO庫文件
#import "c:\program files\common files\system\ado\msado15.dll"no_namespaces rename("EOF" adoEOF")
這行語句聲明在工程中使用ADO,但不使用ADO的名字空間,并且為了避免常數(shù)沖突,將常數(shù)EOF改名為adoEOF。現(xiàn)在不需添加另外的頭文件,就可以使用ADO接口了。
2、初始化OLE/COM庫環(huán)境?
必須注意的是,ADO庫是一組COM動態(tài)庫,這意味應(yīng)用程序在調(diào)用ADO前,必須初始化OLE/COM庫環(huán)境。在MFC應(yīng)用程序里,一個比較好的方法是在應(yīng)用程序主類的InitInstance成員函數(shù)里初始化OLE/COM庫環(huán)境。
BOOL CMyAdoTestApp::InitInstance()?
{?
if(!AfxOleInit())//這就是初始化COM庫?
{?
AfxMessageBox(“OLE初始化出錯!”);?
return FALSE;?
}
……
}
3、ADO接口簡介
ADO庫包含三個基本接口:_ConnectionPtr接口、_CommandPtr接口和_RecordsetPtr接口。?
_ConnectionPtr接口返回一個記錄集或一個空指針。通常使用它來創(chuàng)建一個數(shù)據(jù)連接或執(zhí)行一條不返回任何結(jié)果的SQL語句,如一個存儲過程。使用_ConnectionPtr接口返回一個記錄集不是一個好的使用方法。對于要返回記錄的操作通常用_RecordserPtr來實現(xiàn)。而用_ConnectionPtr操作時要想得到記錄條數(shù)得遍歷所有記錄,而用_RecordserPtr時不需要。
_CommandPtr接口返回一個記錄集。它提供了一種簡單的方法來執(zhí)行返回記錄集的存儲過程和SQL語句。在使用_CommandPtr接口時,你可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用連接串。如果你只執(zhí)行一次或幾次數(shù)據(jù)訪問操作,后者是比較好的選擇。但如果你要頻繁訪問數(shù)據(jù)庫,并要返回很多記錄集,那么,你應(yīng)該使用全局_ConnectionPtr接口創(chuàng)建一個數(shù)據(jù)連接,然后使用_CommandPtr接口執(zhí)行存儲過程和SQL語句。
_RecordsetPtr是一個記錄集對象。與以上兩種對象相比,它對記錄集提供了更多的控制功能,如記錄鎖定,游標控制等。同_CommandPtr接口一樣,它不一定要使用一個已經(jīng)創(chuàng)建的數(shù)據(jù)連接,可以用一個連接串代替連接指針賦給_RecordsetPtr的connection成員變量,讓它自己創(chuàng)建數(shù)據(jù)連接。如果你要使用多個記錄集,最好的方法是同Command對象一樣使用已經(jīng)創(chuàng)建了數(shù)據(jù)連接的全局_ConnectionPtr接口?
,然后使用_RecordsetPtr執(zhí)行存儲過程和SQL語句。
4、使用_ConnectionPtr接口?
_ConnectionPtr主要是一個連接接口,取得與數(shù)據(jù)庫的連接。它的連接字符串可以是自己直接寫,也可以指向一個ODBC DSN。。?
? _ConnectionPtr pConn;?
if (FAILED(pConn.CreateInstance("ADODB.Connection")))?
{?
AfxMessageBox("Create Instance failed!");?
return;?
}
CString strSRC;?
strSRC="Driver=SQL Server;Server=";?
strSRC+="suppersoft";?
strSRC+=";Database=";?
strSRC+="mydb";?
strSRC+=";UID=SA;PWD=";
CString strSQL = "Insert into student(no,name,sex,address) values(3,'aaa','male','beijing')";
_variant_t varSRC(strSRC);?
_variant_t varSQL(strSQL);?
_bstr_t bstrSRC(strSRC);
if (FAILED(pConn->Open(bstrSRC,"","",-1)))?
{?
AfxMessageBox("Can not open Database!");?
pConn.Release();?
return;?
}
COleVariant vtOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);
pConn->Execute(_bstr_t(strSQL),&vtOptional,-1);
pConn.Release();
AfxMessageBox("ok!");
5、使用_RecordsetPtr接口(以連接SQL Server為例)?
_RecordsetPtr pPtr;?
if (FAILED(pPtr.CreateInstance("ADODB.Recordset")))?
{?
AfxMessageBox("Create Instance failed!");?
return FALSE;?
}
CString strSRC;?
strSRC="Driver=SQL Server;Server=";?
strSRC+="210.46.141.145";?
strSRC+=";Database=";?
strSRC+="mydb";?
strSRC+=";UID=sa;PWD=";?
strSRC+="sa";
CString strSQL = "select id,name,gender,address from personal";
_variant_t varSRC(strSRC);?
_variant_t varSQL(strSQL);
if(FAILED(pPtr->Open(varSQL,varSRC,adOpenStatic,adLockOptimistic,adCmdText)))?
{?
AfxMessageBox("Open table failed!");?
pPtr.Release();?
return FALSE;?
}
while(!pPtr->GetadoEOF())?
{?
_variant_t varNo;?
_variant_t varName;?
_variant_t varSex;?
_variant_t varAddress;
varNo = pPtr->GetCollect ("id");?
varName = pPtr->GetCollect ("name");?
varSex = pPtr->GetCollect ("gender");?
varAddress = pPtr->GetCollect ("address");
CString strNo =(char *)_bstr_t(varNo);?
CString strName =(char *)_bstr_t(varName);?
CString strSex =(char *)_bstr_t(varSex);?
CString strAddress =(char *)_bstr_t(varAddress);
strNo.TrimRight();?
strName.TrimRight();?
strSex.TrimRight();?
strAddress.TrimRight();
int nCount = m_list.GetItemCount();?
int nItem = m_list.InsertItem (nCount,_T(""));?
m_list.SetItemText (nItem,0,strNo);?
m_list.SetItemText (nItem,1,strName);?
m_list.SetItemText (nItem,2,strSex);?
m_list.SetItemText (nItem,3,strAddress);
pPtr->MoveNext();?
}
pPtr->Close();?
pPtr.Release();
6、使用_CommandPtr接口?
_CommandPtr接口返回一個Recordset對象,并且提供了更多的記錄集控制功能,以下代碼示例了使用_CommandPtr接口的方法:
代碼11:使用_CommandPtr接口獲取數(shù)據(jù)?
_CommandPtr pCommand;?
_RecordsetPtr pRs;?
pCommand.CreateInstance(__uuidof(Command));?
pCommand->ActiveConnection=pConn;?
pCommand->CommandText="select * from student";?
pCommand->CommandType=adCmdText;?
pCommand->Parameters->Refresh();?
pRs=pCommand->Execute(NULL,NULL,adCmdUnknown);?
_variant_t varValue = pRs->GetCollect("name");?
CString strValue=(char*)_bstr_t(varValue);
6、關(guān)于數(shù)據(jù)類型轉(zhuǎn)換由于COM對象是跨平臺的,它使用了一種通用的方法來處理各種類型的數(shù)據(jù),?
因此Cstring 類和COM對象是不兼容的,我們需要一組API來轉(zhuǎn)換COM對象和C++類型的數(shù)據(jù)。_vatiant_t和_bstr_t就是這樣兩種對象。它們提供了通用的方法轉(zhuǎn)換COM對象和C++類型的數(shù)據(jù)。
............................................................................................
1. 生成應(yīng)用程序框架并初始化OLE/COM庫環(huán)境?
創(chuàng)建一個標準的MFC AppWizard(exe)應(yīng)用程序,然后在使用ADO數(shù)據(jù)庫的InitInstance函數(shù)中初始化OLE/COM庫(因為ADO庫是一個COM DLL庫)。?
本例為:
BOOL CAdotestDlg::OnInitDialog()?
{?
? ? ? ? ::CoInitialize(NULL); //初始化OLE/COM庫環(huán)境?
? }
程序最后要調(diào)用 ::CoUninitialize();//釋放程序占用的COM 資源。
另外:
m_pRecordset->Close(); 注意!!!不要多次關(guān)閉!!!!!!!!!!!!?
m_pConnection->Close();?
m_pRecordset = NULL;?
m_pConnection = NULL;?
2. 引入ADO庫文件
使用ADO前必須在工程的stdafx.h文件最后用直接引入符號#import引入ADO庫文件,以使編譯器能正確編譯。代碼如下:?
#import "C:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")?
ADO類的定義是作為一種資源存儲在ADO DLL(msado15.dll)中,在其內(nèi)部稱為類型庫。類型庫描述了自治接口,以及C++使用的COM vtable接口。當使用#import指令時,在運行時Visual C++需要從ADO DLL中讀取這個類型庫,并以此創(chuàng)建一組C++頭文件。這些頭文件具有.tli 和.tlh擴展名,讀者可以在項目的目錄下找到這兩個文件。在C++程序代碼中調(diào)用的ADO類要在這些文件中定義。?
? 程序的第三行指示ADO對象不使用名稱空間。在有些應(yīng)用程序中,由于應(yīng)用程序中的對象與ADO中的對象之間可能會出現(xiàn)命名沖突,所以有必要使用名稱空間。如果要使用名稱空間,則可把第三行程序修改為: rename_namespace("AdoNS")。第四行代碼將ADO中的EOF(文件結(jié)束)更名為adoEOF,以避免與定義了自己的EOF的其他庫沖突。
3.利用智能指針進行數(shù)據(jù)庫操作
在CaboutDlg頭文件中定義兩個ADO智能指針類實例,并在對話框中加入一個ListCtrl。
class CAdotestDlg : public CDialog?
{?
? ? ?_ConnectionPtr m_pConnection;?
? ? ?_RecordsetPtr m_pRecordset;?
ClistCtrl m_List;?
? ? ?......?
} ??
ADO庫包含三個智能指針:_ConnectionPtr、_CommandPtr和_RecordsetPtr。
_ConnectionPtr通常被用來創(chuàng)建一個數(shù)據(jù)連接或執(zhí)行一條不返回任何結(jié)果的SQL語句,如一個存儲過程。?
_CommandPtr返回一個記錄集。它提供了一種簡單的方法來執(zhí)行返回記錄集的存儲過程和SQL語句。在使用_CommandPtr接口時,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用連接串。 _RecordsetPtr是一個記錄集對象。與以上兩種對象相比,它對記錄集提供了更多的控制功能,如記錄鎖定、游標控制等。
在使用ADO程序的事件響應(yīng)中OnButton1加入以下代碼:
void CAdotestDlg::OnButton1()?
{?
m_List.ResetContent();?
m_pConnection.CreateInstance(_uuidof(Connection)); //初始化Connection指針?
m_pRecordset.CreateInstance(_uuidof(Recordset));//初始化Recordset指針?
try?
{?
? m_pConnection->Open("DSN=ADOTest","","",0); //連接叫作ADOTest的ODBC數(shù)據(jù)源?
? //注意:這是連接不需要用戶ID或密碼的open 函數(shù)?
? // 否則形式為 ->Open("DSN=test;uid=sa;pwd=123;","","",0);?
? // 執(zhí)行SQL語句得到一個記錄集把其指針賦值給m_pRecordset?
? CString strSql="select * from middle";?
? BSTR bstrSQL = strSql.AllocSysString();?
? m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,adCmdText);?
? //adOpenDynamic:動態(tài) adLockOptimistic樂觀封鎖法 adCmdText:文本查詢語句?
? while(!m_pRecordset->adoEOF)//遍歷所有記錄?
? {?
? ?//取紀錄字段值方式之一?
? ?_variant_t TheValue; //VARIANT數(shù)據(jù)類型?
? ?TheValue = m_pRecordset->GetCollect("BIG_NAME");//得到字段BIG_NAME的值?
? ?if(TheValue.vt!=VT_NULL)?
? ? m_List.AddString((char*)_bstr_t(TheValue));?
? ?//將該值加入到列表控件中?
? ?//取紀錄字段值方式之二?
? ?// _bstr_t TheValue1=m_pRecordset->Fields->GetItem("BIG_NAME")->Value;?
? ?// CString temp=TheValue1.copy();?
? ?// m_List.AddString(temp);?
? ?//數(shù)據(jù)類型轉(zhuǎn)換?
? ?_variant_t vUsername,vBirthday,vID,vOld;?
? ?TRACE("id:%d,姓名:%s,年齡:%d,生日:%s\r\n",?
? ?vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday);
? ?m_pRecordset->MoveNext();//轉(zhuǎn)到下一條紀錄?
? }?
? m_pRecordset->Close();?
? m_pConnection->Close();?
}?
catch (_com_error e)//異常處理?
{?
? AfxMessageBox(e.ErrorMessage());?
}?
m_pRecordset->Close(); //注意!!!不要多次關(guān)閉!!!!否則會出錯?
m_pConnection->Close();?
m_pRecordset = NULL;?
m_pConnection = NULL;?
}
程序中通過_variant_t和_bstr_t轉(zhuǎn)換COM對象和C++類型的數(shù)據(jù), _variant_t類封裝了OLE自治VARIANT數(shù)據(jù)類型。在C++中使用_variant_t類要比直接使用VARIANT數(shù)據(jù)類型容易得多。
好,編譯后該程序就能運行了,但記住運行前要創(chuàng)建一個叫ADOTest的ODBC數(shù)據(jù)源。該程序?qū)驯韒iddle中的BIG_NAME字段值顯示在列表控件中。
在Visual C++中用ADO進行數(shù)據(jù)庫編程(中)?
4.執(zhí)行SQL命令并取得結(jié)果記錄集
? ? 為了取得結(jié)果記錄集,我們定義一個指向Recordset對象的指針:_RecordsetPtr m_pRecordset;?
并為其創(chuàng)建Recordset對象的實例: m_pRecordset.CreateInstance("ADODB.Recordset");?
SQL命令的執(zhí)行可以采用多種形式,下面我們一進行闡述。
(1)利用Connection對象的Execute方法執(zhí)行SQL命令
Execute方法的原型如下所示:
? ? ?_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options )
? ? ?其中CommandText是命令字串,通常是SQL命令。?
參數(shù)RecordsAffected是操作完成后所影響的行數(shù),?
參數(shù)Options表示CommandText中內(nèi)容的類型,Options可以取如下值之一:?
adCmdText:表明CommandText是文本命令?
adCmdTable:表明CommandText是一個表名?
adCmdProc:表明CommandText是一個存儲過程?
adCmdUnknown:未知
? ? Execute執(zhí)行完后返回一個指向記錄集的指針,下面我們給出具體代碼并作說明。?
? ? _variant_t RecordsAffected;?
? ? ///執(zhí)行SQL命令:CREATE TABLE創(chuàng)建表格users,users包含四個字段:整形ID,字符串username,整形old,日期型birthday?
? ? m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",?
? ?&RecordsAffected,?
? ?adCmdText);
? ? ///往表格里面添加記錄?
? ? m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) VALUES (1, """"Washington"""",25,""""1970/1/1"""")",&RecordsAffected,adCmdText);
? ? ///將所有記錄old字段的值加一?
? ? m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText);
? ? ///執(zhí)行SQL統(tǒng)計命令得到包含記錄條數(shù)的記錄集?
? ? m_pRecordset = ?m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText);?
? ? _variant_t vIndex = (long)0;?
? ? _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一個字段的值放入vCount變量?
? ? 上兩句可以寫成— _variant_t vCount = m_pRecordset->GetCollect((_variant_t)((long)0));?
? ? m_pRecordset->Close();///關(guān)閉記錄集?
? ? CString message;?
? ? message.Format("共有%d條記錄",vCount.lVal);?
? ? AfxMessageBox(message);///顯示當前記錄條數(shù)
(2)利用Command對象來執(zhí)行SQL命令
_CommandPtr m_pCommand;?
m_pCommand.CreateInstance("ADODB.Command");?
_variant_t vNULL;?
vNULL.vt = VT_ERROR;?
vNULL.scode = DISP_E_PARAMNOTFOUND;///定義為無參數(shù)?
m_pCommand->ActiveConnection = m_pConnection;///非常關(guān)鍵的一句,將建立的連接賦值給它?
m_pCommand->CommandText = "SELECT * FROM users";///命令字串?
m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///執(zhí)行命令,取得記錄集
在這段代碼中我們只是用Command對象來執(zhí)行了SELECT查詢語句,Command對象在進行存儲過程的調(diào)用中能真正體現(xiàn)它的作用。下次我們將詳細介紹。
(3)直接用Recordset對象進行查詢?nèi)〉糜涗浖?
實例——
void CGmsaDlg::OnDBSelect()?
{?
? ? // TODO: Add your control notification handler code here?
? ? ?_RecordsetPtr Rs1; ?//定義Recordset對象?
? ? _bstr_t Connect("DSN=GMS;UID=sa;PWD=;");//定義連接字符串?
? ? _bstr_t Source ("SELECT count(*) FROM buaa.mdb010"); ?//要執(zhí)行的SQL語句?
? ? ::CoInitialize(NULL); ? ?//初始化Rs1對象?
? ? ? ? HRESUL hr = Rs1.CreateInstance( __uuidof( Recordset ) );?
? ? ? ?//省略對返回值hr的判斷?
? ? ?Rs1->Open( Source,?
? ? ? ? ? ? Connect,?
? ? ? ? ? ? ? ? adOpenForwardOnly,?
? ? ? ? ? ? ? ? ? ? adLockReadOnly,?
? ? ? ? ? ? -1 );?
? ? _variant_t temp=Rs1->GetCollect(_variant_t((long)0));?
? ? CString strTemp=(char* )(_bstr_t)temp;?
? ? MessageBox("OK!"+strTemp);?
}
例如?
? m_pRecordset->Open("SELECT * FROM users",?
? _variant_t((IDispatch *)m_pConnection,true),?
? adOpenStatic,?
? adLockOptimistic,?
? adCmdText);
Open方法的原型是這樣的:?
HRESULT Recordset15::Open ( const _variant_t & Source,?
? ?const _variant_t & ActiveConnection,?
? ?enum CursorTypeEnum CursorType,?
? ?enum LockTypeEnum LockType,?
? ?long Options )
其中:
1Source是數(shù)據(jù)查詢字符串?
2ActiveConnection是已經(jīng)建立好的連接(我們需要用Connection對象指針來構(gòu)造一個_variant_t對象)?
3CursorType光標類型,它可以是以下值之一,請看這個枚舉結(jié)構(gòu):
enum CursorTypeEnum?
{?
adOpenUnspecified = -1,///不作特別指定?
adOpenForwardOnly = 0,///前滾靜態(tài)光標。這種光標只能向前瀏覽記錄集,比如用MoveNext向前滾動,這種方式可以提高瀏覽速度。但諸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用?
adOpenKeyset = 1,///采用這種光標的記錄集看不到其它用戶的新增、刪除操作,但對于更新原有記錄的操作對你是可見的。
adOpenDynamic = 2,///動態(tài)光標。所有數(shù)據(jù)庫的操作都會立即在各用戶記錄集上反應(yīng)出來。?
adOpenStatic = 3///靜態(tài)光標。它為你的記錄集產(chǎn)生一個靜態(tài)備份,但其它用戶的新增、刪除、更新操作對你的記錄集來說是不可見的。?
};?
4LockType鎖定類型,它可以是以下值之一,請看如下枚舉結(jié)構(gòu):
enum LockTypeEnum?
{?
adLockUnspecified = -1,///未指定?
adLockReadOnly = 1,///只讀記錄集?
adLockPessimistic = 2,悲觀鎖定方式。數(shù)據(jù)在更新時鎖定其它所有動作,這是最安全的鎖定機制?
adLockOptimistic = 3,樂觀鎖定方式。只有在你調(diào)用Update方法時才鎖定記錄。在此之前仍然可以做數(shù)據(jù)的更新、插入、刪除等動作?
adLockBatchOptimistic = 4,樂觀分批更新。編輯時記錄不會鎖定,更改、插入及刪除是在批處理模式下完成。?
};
5Options可以取如下值之一:?
adCmdText:表明CommandText是文本命令?
adCmdTable:表明CommandText是一個表名?
adCmdProc:表明CommandText是一個存儲過程?
adCmdUnknown:未知
10、邦定數(shù)據(jù)
定義一個綁定類,將其成員變量綁定到一個指定的記錄集,以方便于訪問記錄集的字段值。
(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宏關(guān)聯(lián)起來。每個字段對應(yīng)于兩個變量,一個存放字段的值,另一個存放字段的狀態(tài)。字段用從1開始的序號表示,如1,2,3等等。
特別要注意的是:如果要綁定的字段是字符串類型,則對應(yīng)的字符數(shù)組的元素個數(shù)一定要比字段長度大2(比如m_szau_fname[22],其綁定的字段au_fname的長度實際是20),不這樣綁定就會失敗。我分析多出的2可能是為了存放字符串結(jié)尾的空字符null和BSTR字符串開頭的一個字(表示BSTR的長度)。這個問題對于初學(xué)者來說可能是一個意想不到的問題。
CADORecordBinding類的定義在icrsint.h文件里,內(nèi)容是:
class CADORecordBinding?
{?
public:?
STDMETHOD_(const ADO_BINDING_ENTRY*, GetADOBindingEntries) (VOID) PURE;?
};
BEGIN_ADO_BINDING宏的定義也在icrsint.h文件里,內(nèi)容是:?
#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接口才能綁定,調(diào)用它的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();
只要字段的狀態(tài)是adFldOK,就可以訪問。如果修改了字段,不要忘了先調(diào)用picRs的Update(注意不是Recordset的Update),然后才關(guān)閉,也不要忘了釋放picRs(即picRs->Release();)。
(4). 此時還可以用IADORecordBinding接口添加新紀錄
if(FAILED(picRs->AddNew(&rs)))?
......
11. 訪問長數(shù)據(jù)
在Microsoft SQL中的長數(shù)據(jù)包括text、image等這樣長類型的數(shù)據(jù),作為二進制字節(jié)來對待。
可以用Field對象的GetChunk和AppendChunk方法來訪問。每次可以讀出或?qū)懭肴繑?shù)據(jù)的一部分,它會記住上次訪問的位置。但是如果中間訪問了別的字段后,就又得從頭來了。
請看下面的例子:
//寫入一張照片到數(shù)據(jù)庫:?
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();
//從數(shù)據(jù)庫讀一張照片:?
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問題
學(xué)會使用SafeArray也是很重要的,因為在ADO編程中經(jīng)常要用。它的主要目的是用于automation中的數(shù)組型參數(shù)的傳遞。因為在網(wǎng)絡(luò)環(huán)境中,數(shù)組是不能直接傳遞的,而必須將其包裝成SafeArray。實質(zhì)上SafeArray就是將通常的數(shù)組增加一個描述符,說明其維數(shù)、長度、邊界、元素類型等信息。SafeArray也并不單獨使用,而是將其再包裝到VARIANT類型的變量中,然后才作為參數(shù)傳送出去。在VARIANT的vt成員的值如果包含VT_ARRAY│...,那么它所封裝的就是一個SafeArray,它的parray成員即是指向SafeArray的指針。SafeArray中元素的類型可以是VARIANT能封裝的任何類型,包括VARIANT類型本身。
使用SafeArray的具體步驟:
方法一:
包裝一個SafeArray:
(1). 定義變量,如:
VARIANT varChunk;?
SAFEARRAY *psa;?
SAFEARRAYBOUND rgsabound[1];
(2). 創(chuàng)建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). 放置數(shù)據(jù)元素到SafeArray:
for(long index=0;index<uIsRead;index++)?
{?
if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index])))?
::MessageBox(NULL,"出毛病了。","提示",MB_OK │ MB_ICONWARNING);?
}
一個一個地放,挺麻煩的。
(4). 封裝到VARIANT內(nèi):
varChunk.vt = VT_ARRAY│VT_UI1;?
varChunk.parray = psa;
這樣就可以將varChunk作為參數(shù)傳送出去了。
讀取SafeArray中的數(shù)據(jù)的步驟:
(1). 用SafeArrayGetElement一個一個地讀
BYTE buf[lIsRead];?
for(long index=0;index<lIsRead;index++)?
{?
::SafeArrayGetElement(varChunk.parray,&index,buf+index);?
}
就讀到緩沖區(qū)buf里了。
方法二:
使用SafeArrayAccessData直接讀寫SafeArray的緩沖區(qū):
(1). 讀緩沖區(qū):
BYTE *buf;?
SafeArrayAccessData(varChunk.parray, (void **)&buf);?
f.Write(buf,lIsRead);?
SafeArrayUnaccessData(varChunk.parray);
(2). 寫緩沖區(qū):
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的數(shù)據(jù)緩沖區(qū),比用SafeArrayGetElement和SafeArrayPutElement速度快。特別適合于讀取數(shù)據(jù)。但用完之后不要忘了調(diào)用::SafeArrayUnaccessData(psa),否則會出錯的。
13. 使用書簽( bookmark )
書簽可以唯一標識記錄集中的一個記錄,用于快速地將當前記錄移回到已訪問過的記錄,以及進行過濾等等。Provider會自動為記錄集中的每一條記錄產(chǎn)生一個書簽,我們只需要使用它就行了。我們不能試圖顯示、修改或比較書簽。ADO用記錄集的Bookmark屬性表示當前記錄的書簽。
用法步驟:
(1). 建立一個VARIANT類型的變量
_variant_t VarBookmark;
(2). 將當前記錄的書簽值存入該變量
也就是記錄集的Bookmark屬性的當前值。
VarBookmark = rst->Bookmark;
(3). 返回到先前的記錄
將保存的書簽值設(shè)置到記錄集的書簽屬性中:
// Check for whether bookmark set for a record?
if (VarBookmark.vt == VT_EMPTY)?
printf("No Bookmark set!\n");?
else?
rst->Bookmark = VarBookmark;
設(shè)置完后,當前記錄即會移動到該書簽指向的記錄。
14、設(shè)置過濾條件
Recordset對象的Filter屬性表示了當前的過濾條件。它的值可以是以AND或OR連接起來的條件表達式(不含WHERE關(guān)鍵字)、由書簽組成的數(shù)組或ADO提供的FilterGroupEnum枚舉值。為Filter屬性設(shè)置新值后Recordset的當前記錄指針會自動移動到滿足過濾條件的第一個記錄。例如:
rst->Filter = _bstr_t ("姓名='趙薇' AND 性別=’女’");
在使用條件表達式時應(yīng)注意下列問題:
(1)、可以用圓括號組成復(fù)雜的表達式
例如:
rst->Filter = _bstr_t ("(姓名='趙薇' AND 性別=’女’) OR AGE<25");
但是微軟不允許在括號內(nèi)用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,如果過濾條件是由書簽組成的數(shù)組,則需將該數(shù)組轉(zhuǎn)換為SafeArray,然后再封裝到一個VARIANT或_variant_t型的變量中,再賦給Filter屬性。
15、索引與排序
(1)、建立索引
當以某個字段為關(guān)鍵字用Find方法查找時,為了加快速度可以以該字段為關(guān)鍵字在記錄集內(nèi)部臨時建立索引。只要將該字段的Optimize屬性設(shè)置為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中稱為動態(tài)屬性),ADO本身沒有此屬性。
(2)、排序
要排序也很簡單,只要把要排序的關(guān)鍵字列表設(shè)置到Recordset對象的Sort屬性里即可,例如:
pRstAuthors->CursorLocation = adUseClient;?
pRstAuthors->Open("SELECT * FROM mytable",?
_variant_t((IDispatch *) pConnection),?
adOpenStatic, adLockReadOnly, adCmdText);?
......?
pRst->Sort = "姓名 DESC, 年齡 ASC";
關(guān)鍵字(即字段名)之間用逗號隔開,如果要以某關(guān)鍵字降序排序,則應(yīng)在該關(guān)鍵字后加一空格,再加DESC(如上例)。升序時ASC加不加無所謂。本操作是利用索引進行的,并未進行物理排序,所以效率較高。?
但要注意,在打開記錄集之前必須將記錄集的CursorLocation屬性設(shè)置為adUseClient,如上例所示。Sort屬性值在需要時隨時可以修改。
16、事務(wù)處理
ADO中的事務(wù)處理也很簡單,只需分別在適當?shù)奈恢谜{(diào)用Connection對象的三個方法即可,這三個方法是:
(1)、在事務(wù)開始時調(diào)用
pCnn->BeginTrans();
(2)、在事務(wù)結(jié)束并成功時調(diào)用
pCnn->CommitTrans ();
(3)、在事務(wù)結(jié)束并失敗時調(diào)用
pCnn->RollbackTrans ();
在使用事務(wù)處理時,應(yīng)盡量減小事務(wù)的范圍,即減小從事務(wù)開始到結(jié)束(提交或回滾)之間的時間間隔,以便提高系統(tǒng)效率。需要時也可在調(diào)用BeginTrans()方法之前,先設(shè)置Connection對象的IsolationLevel屬性值,詳細內(nèi)容參見MSDN中有關(guān)ADO的技術(shù)資料。
三、使用ADO編程常見問題解答
以下均是針對MS SQL 7.0編程時所遇問題進行討論。
1、連接失敗可能原因
Enterprise Managemer內(nèi),打開將服務(wù)器的屬性對話框,在Security選項卡中,有一個選項Authentication。
如果該選項是Windows NT only,則你的程序所用的連接字符串就一定要包含Trusted_Connection參數(shù),并且其值必須為yes,如:
"Provider=SQLOLEDB;Server=888;Trusted_Connection=yes"?
";Database=master;uid=lad;";
如果不按上述操作,程序運行時連接必然失敗。
如果Authentication選項是SQL Server and Windows NT,則你的程序所用的連接字符串可以不包含Trusted_Connection參數(shù),如:
"Provider=SQLOLEDB;Server=888;Database=master;uid=lad;pwd=111;";
因為ADO給該參數(shù)取的默認值就是no,所以可以省略。我認為還是取默認值比較安全一些。
2、改變當前數(shù)據(jù)庫的方法
使用Tansct-SQL中的USE語句即可。
3、如何判斷一個數(shù)據(jù)庫是否存在
(1)、可打開master數(shù)據(jù)庫中一個叫做SCHEMATA的視圖,其內(nèi)容列出了該服務(wù)器上所有的數(shù)據(jù)庫名稱。
(2) 、更簡便的方法是使用USE語句,成功了就存在;不成功,就不存在。例如:
try{?
m_pConnect->Execute ( _bstr_t("USE INSURANCE_2002"),NULL,?
adCmdText│adExecuteNoRecords );?
}?
catch (_com_error &e)?
{?
blSuccess=FALSE;?
CString str="數(shù)據(jù)庫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服務(wù)器上的每個數(shù)據(jù)庫中都有一個名為sysobjects的表,查看此表的內(nèi)容即知指定的表是否在該數(shù)據(jù)庫中。
(3)、同樣,每個數(shù)據(jù)庫中都有一個名為TABLES的視圖(View),查看此視圖的內(nèi)容即知指定的表是否在該數(shù)據(jù)庫中。
5、類型轉(zhuǎn)換問題
(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類的類型轉(zhuǎn)換
_ConnectionPtr可以自動轉(zhuǎn)換成IDspatch*類型,這是因為_ConnectionPtr實際上是_com_ptr_t類的一個實例,而這個類有此類型轉(zhuǎn)換函數(shù)。
同理,_RecordsetPtr和_CommandPtr也都可以這樣轉(zhuǎn)換。
(3)、_bstr_t和_variant_t類
在ADO編程時,_bstr_t和_variant_t這兩個類很有用,省去了許多BSTR和VARIANT類型轉(zhuǎn)換的麻煩。
6、打開記錄集時的問題
在打開記錄集時,在調(diào)用Recordset的Open方法時,其最后一個參數(shù)里一定不能包含adAsyncExecute,否則將因為是異步操作,在讀取數(shù)據(jù)時無法讀到數(shù)據(jù)。
7、異常處理問題
對所有調(diào)用ADO的語句一定要用try和catch語句捕捉異常,否則在發(fā)生異常時,程序會異常退出。
8、使用SafeArray問題
在初學(xué)使用中,我曾遇到一個傷腦筋的問題,一定要注意:
在定義了SAFEARRAY的指針后,如果打算重復(fù)使用多次,則在中間可以調(diào)用::SafeArrayDestroyData釋放數(shù)據(jù),但決不能調(diào)用::SafeArrayDestroyDescriptor,否則必然出錯,即使調(diào)用SafeArrayCreate也不行。例如:
SAFEARRAY *psa;?
......?
//When the data are no longer to be used:?
::SafeArrayDestroyData( psa);
我分析在定義psa指針時,一個SAFEARRAY的實例(也就是SAFEARRAY描述符)也同時被自動建立了。但是只要一調(diào)用::SafeArrayDestroyDescriptor,描述符就被銷毀了。
所以我認為::SafeArrayDestroyDescriptor可以根本就不調(diào)用,即使調(diào)用也必須在最后調(diào)用。
9、重復(fù)使用命令對象問題
一個命令對象如果要重復(fù)使用多次(尤其是帶參數(shù)的命令),則在第一次執(zhí)行之前,應(yīng)將它的Prepared屬性設(shè)置為TRUE。這樣會使第一次執(zhí)行減慢,但卻可以使以后的執(zhí)行全部加快。
10、綁定字符串型字段問題
如果要綁定的字段是字符串類型,則對應(yīng)的字符數(shù)組的元素個數(shù)一定要比字段長度大2(比如m_szau_fname[22],其綁定的字段au_fname的長度實際是20),不這樣綁定就會失敗。
11、使用AppendChunk的問題
當用AddNew方法剛剛向記錄集內(nèi)添加一個新記錄之后,不能首先向一個長數(shù)據(jù)字段(image類型)寫入數(shù)據(jù),必須先向其他字段寫入過數(shù)據(jù)之后,才能調(diào)用AppendChunk寫該字段,否則出錯。也就是說,AppendChunk不能緊接在AddNew之后。另外,寫入其他字段后還必須緊接著調(diào)用AppendChunk,而不能調(diào)用記錄集的Update方法后,才調(diào)用AppendChunk,否則調(diào)用AppendChunk時也會出錯。換句話說,就是必須AppendChunk在前,Update在后。因而這個時候就不能使用帶參數(shù)的AddNew了,因為帶參數(shù)的AddNew會自動調(diào)用記錄集的Update,所以AppendChunk就跑到Update的后面了,就只有出錯了!因此,這時應(yīng)該用不帶參數(shù)的AddNew。
我推測這可能是MS SQL 7.0的問題,在MS SQL 2000中則不存在這些問題,但是AppendChunk仍然不能在Update之后。
四、小結(jié)
一般情況下,Connection和Command的Execute用于執(zhí)行不產(chǎn)生記錄集的命令,而Recordset的Open用于產(chǎn)生一個記錄集,當然也不是絕對的。特別Command主要是用于執(zhí)行參數(shù)化的命令,可以直接由Command對象執(zhí)行,也可以將Command對象傳遞給Recordset的Open。
本文中的代碼片斷均在VC++ 6.0、Windows NT 4.0 SP6和MS SQL 7.0中調(diào)試通過。相信您讀過之后,編寫簡單的數(shù)據(jù)庫程序應(yīng)該沒有問題了。當然要編寫比較實用的、復(fù)雜一點的程序,還需要對OLE DB、ADO以及數(shù)據(jù)庫平臺再多了解一點,希望您繼續(xù)努力,一定會很快成功的!詳細參考資料請參見微軟MSDN July 2000光盤或MS SQL 7.0在線文檔資料(Books online)。文中難免有錯誤和不妥之處,敬請各位批評指正!
............................................................................................?
ADO 數(shù)據(jù)庫連接-實例分析-VC?
因為是筆記,所以不一定很有序,貼出來的意思一方面是可以給大家一個參考,一邊也有希望大家一起來完善的意思,其中錯誤和不足之處當然希望大家可以及時地通知我,在貼上來的時候,我有一個想法,因為我在使用的過程中,總是會遇上一些莫明其妙的錯誤,所以我想大家有可能也會遇上,于是,我給出了一個錯誤總結(jié),當然這里就需要大家一起來完善了,如果大家有什么心得和見解,希望在評論中留言,我會及時地接受大家的意見并把他們加進來(當然是在得到您的允許的情況下)
1、導(dǎo)入庫文件使用ADO前必須在工程的stdafx.h文件最后用直接引入符號#import引入ADO庫文件,以使編譯器能正確編譯。代碼如下:
#import "C:Program Filescommon filessystemadomsado15.dll" no_namespace rename("EOF","EndOfFile") rename("BOF","FirstOfFile")
ADO類的定義是作為一種資源存儲在ADO DLL(msado15.dll)中,在其內(nèi)部稱為類型庫。類型庫描述了自治接口,以及C++使用的COM vtable接口。當使用#import指令時,在運行時Visual C++需要從ADO DLL中讀取這個類型庫,并以此創(chuàng)建一組C++頭文件。這些頭文件具有.tli 和.tlh擴展名,讀者可以在項目的目錄下找到這兩個文件。在C++程序代碼中調(diào)用的ADO類要在這些文件中定義。
程序的第三行指示ADO對象不使用名稱空間。在有些應(yīng)用程序中,由于應(yīng)用程序中的對象與ADO中的對象之間可能會出現(xiàn)命名沖突,所以有必要使用名稱空間。如果要使用名稱空間,則可把第三行程序修改為: rename_namespace("AdoNS")。第四行代碼將ADO中的EOF(文件結(jié)束)更名為adoEOF,以避免與定義了自己的EOF的其他庫沖突。
2、初始化COM環(huán)境
(1)::CoInitialize(NULL); //初始化OLE/COM庫環(huán)境
:: CoUninitialize();//既然初始化了環(huán)境,當然就有必要釋放他了
(2)也可以調(diào)用MFC全局函數(shù)
AfxOleInit();
3、三大對象的定義和創(chuàng)建實例
(1) _ConnectionPtr pConnection("ADODB.Connection");
_RecordsetPtr pRecordset("ADODB.Recordset");
_CommandPtr pCommand("ADODN.Command");
(2) _ConnectionPtr pConnection;
_RecordsetPtr pRecordset;
_CommandPtr pCommand;
pConnection.CreateInstance(__uuidof(Connection));
pRecordset.CreateInstance(__uuidof(Recordset));
pCommand.CreateInstance(__uuidof(Command));
(3) _ConnectionPtr pConnection;
_RecordsetPtr pRecordset;
_CommandPtr pCommand;
pConnection.CreateInstance("ADODB.Connection");
pRecordset.CreateInstance("ADODB.Recordset");
pCommand.CreateInstance("ADODB.Command");
4、打開一個連接
pConnection->Open(ConnectionString,"","",adModeUnknown);///連接數(shù)據(jù)庫
上面的連接字符串ConnectionString根據(jù)不同的數(shù)據(jù)源,分別對應(yīng)不同的寫法
1)訪問Access 2000
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=databaseName;User ID=userName;Password=userPassWord"
2)訪問ODBC數(shù)據(jù)
" Provider=MADASQL;DSN=dsnName;UID=userName;PWD=userPassword;"
3)訪問Oracle數(shù)據(jù)庫
“Provider=MSDAORA;Data Sourse=serverName;User ID=userName;Password=userPassword;"
3)訪問MS SQL數(shù)據(jù)庫
"Provider=SQLOLEDB,Data Source=serverName;Initial Catalog=databaseName;User ID=userName;Password=userPassword;"
4、執(zhí)行SQL命令
SQL命令比較多,但是不去考慮細節(jié),這里只說出通用的方法
CString strSQL;//定義SQL命令串,用來保存SQL語句
strSQL.Format("SQL statement");
然后在每個要用到SQL命令串的方法中,使用strSQL.AllocSysString()的方法進行類型轉(zhuǎn)換
5、com的專用數(shù)據(jù)類型
variant ,bstr ,SafeArray
variant變量的范圍包括很多,使用_variant_t 進行管理
bstr是一種字符串變量,使用_bstr_t進行管理
6、關(guān)閉連接
if(m_pConnection->State)//不能多次關(guān)閉,否則會出現(xiàn)錯誤
m_pConnection->Close();
7、結(jié)構(gòu)化異常處理
ADO封裝了COM接口,所以需要進行錯誤處理
如下例:
HRESULT hr;
try
{
hr = m_pConnection.CreateInstance("ADODB.Connection");///創(chuàng)建Connection對象
if(SUCCEEDED(hr))
{
hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb","","",adModeUnknown);///連接數(shù)據(jù)庫
///上面一句中連接字串中的Provider是針對ACCESS2000環(huán)境的,對于ACCESS97,需要改 為:Provider=Microsoft.Jet.OLEDB.3.51; }
}
catch(_com_error e)///捕捉異常
{
CString errormessage;
errormessage.Format("連接數(shù)據(jù)庫失敗!?
錯誤信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);///顯示錯誤信息
}
8、錯誤原因的分析
(1)不支持接口,可能是不能插入空值
========
相關(guān)鏈接
ODBC部分
http://www.cnblogs.com/cy163/archive/2010/11/16/1878870.html
VC6.0數(shù)據(jù)庫編程之MFC ODBC
http://www.vckbase.com/index.php/wv/248
一個完善的ODBC數(shù)據(jù)庫程序
http://blog.csdn.net/suxinpingtao51/article/details/11742085
MFC ODBC數(shù)據(jù)操作編程
http://www.jb51.net/article/52780.htm
VC實現(xiàn)ODBC數(shù)據(jù)庫操作實例解析
http://blog.163.com/yuyi_vc/blog/static/1703717532010112623058864/
CRecordset類的常用成員函數(shù) 成員變量 以及操作實例?
http://www.it165.net/pro/html/201404/12211.html
CRecordSet操作數(shù)據(jù)庫
http://blog.csdn.net/elcoteq983/article/details/7000745
CRecordset類
ADO部分
http://blog.csdn.net/supermanking/article/details/3082934
VC中調(diào)用ADO對象訪問數(shù)據(jù)庫的范例
========
VC中用ADO和DataGrid控件顯示和更新數(shù)據(jù)庫中的數(shù)據(jù)
VC中用ADO和DataGrid控件顯示和更新數(shù)據(jù)庫中數(shù)據(jù)。
以VC6.0為例
1、新建一對話框
在資源視圖中新建一個對話框,將對話框調(diào)整到適當大小,雙擊對話框創(chuàng)建對話框類。
2、往對話框上添加DataGrid控件
在VC6中點擊 Project->add to project->components and controls。在彈出的對話框中雙擊Registered ActiveX Controls 在彈出的控件列表中選擇Microsoft DataGrid Control 6.0(SP6)(OLEDB),點擊insert,接下來的每一部都點確認按鈕(ok按鈕)。我們發(fā)現(xiàn)在工具箱中多了一個DataGrid控件的圖標。
拖動該圖標到對話框中,并且調(diào)整大小,設(shè)置屬性為AllowAddNew、AllowDelete、AllowUpdate、ColumnHeaders、Enabled。
在DataCrid上右擊用ClassWizard給對話框添加一個DataGrid變量m_dataGrid。
3、 ? ? ? ? ? ? 添加代碼
在對話框頭文件中添加如下代碼#import "C://program files//common files//system//ado//msado15.dll" no_namespace rename ("EOF", "adoEOF")
在對話框頭文件的對話框類中添加如下代碼
_ConnectionPtr ?pConnection;//數(shù)據(jù)庫連接指針
_RecordsetPtr m_pRecordSet;//數(shù)據(jù)集指針
重載對話框OnInitDialog()函數(shù),在其中添加如下代碼
CoInitialize(NULL); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //初始化Com組件 pConnection.CreateInstance(__uuidof(Connection)); ? ? ? ? ? ? ? ? //Connection用于與數(shù)據(jù)庫服務(wù)器的鏈接
? ? ? ?CString conStr; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//數(shù)據(jù)庫連接字符串
? ? ? ?conStr.Format(_T("Driver=SQL SERVER;Database=%s;Server=%s; UID=%s;PWD=%s;"),"test","(local)","sa","sa");//注:此為ADO連接MS SQL數(shù)據(jù)庫的一種方式,如果不是MS SQL數(shù)據(jù)庫則連接方式不同
? ? ? ?/******************連接數(shù)據(jù)庫********************/
? ? ? ?try
? ? ? ?{
? ? ? ? ? ? ? pConnection->ConnectionTimeout = 5; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//設(shè)置連接時間
? ? ? ? ? ? ? pConnection->Open(_bstr_t(conStr),"","",adModeUnknown); ? ? //連接SQL SERVER
? ? ? ?}
? ? ? ?catch(_com_error e) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //捕捉異常
? ? ? ?{
? ? ? ? ? ? ? //AfxMessageBox(e.ErrorMessage());
? ? ? ? ? ? ? return -5;
? ? ? ?}
//創(chuàng)建數(shù)據(jù)集
?
?m_pRecordSet.CreateInstance("ADODB.Recordset");?
m_pRecordSet->CursorLocation= adUseClient;
CString cmdStr;
cmdStr="select pithology as 巖性,patno as 對應(yīng)花紋 from Pithology_Patno";
try
{
m_pRecordSet->Open(_variant_t(cmdStr),_variant_t((IDispatch *)pConnection,true),adOpenKeyset,?
? ?adLockOptimistic, adCmdUnknown);
}
catch (CException e)
{
CString emsg;
e.GetErrorMessage((LPTSTR)&emsg,1);
AfxMessageBox(emsg);
return false;
}
m_dataGrid.SetRefDataSource(NULL);?
m_dataGrid.SetRefDataSource((LPUNKNOWN)m_pRecordSet);?
m_dataGrid.Refresh();?
?CoUninitialize(); ? ? ? ? ? ?
現(xiàn)在DataGrid控件便具有添加、刪除、修改數(shù)據(jù)的功能了。
========
8 ADO一個完整增刪改查模塊的說明
增刪改查4個功能做到一個界面上;
// DlgViewCP.cpp : implementation file //#include "stdafx.h" #include "RMS.h" #include "DlgViewCP.h" #include "DataBinding.h"#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif/ // CDlgViewCP dialogCDlgViewCP::CDlgViewCP(CWnd* pParent /*=NULL*/): CDialog(CDlgViewCP::IDD, pParent) {//{{AFX_DATA_INIT(CDlgViewCP)m_detail = _T("");m_kind = _T("");m_name = _T("");m_no = _T("");m_price = 0.0f;//}}AFX_DATA_INIT }void CDlgViewCP::DoDataExchange(CDataExchange* pDX) {CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CDlgViewCP)DDX_Control(pDX, IDC_LIST_CP, m_list);DDX_Text(pDX, IDC_EDIT_VCDETAIL, m_detail);DDX_Text(pDX, IDC_EDIT_VCKIND, m_kind);DDX_Text(pDX, IDC_EDIT_VCNAME, m_name);DDX_Text(pDX, IDC_EDIT_VCNO, m_no);DDX_Text(pDX, IDC_EDIT_VCPRICE, m_price);//}}AFX_DATA_MAP }BEGIN_MESSAGE_MAP(CDlgViewCP, CDialog)//{{AFX_MSG_MAP(CDlgViewCP)ON_NOTIFY(NM_CLICK, IDC_LIST_CP, OnClickListCp)ON_BN_CLICKED(IDC_BTN_VCADD, OnBtnVcadd)ON_BN_CLICKED(IDC_BTN_VCDEL, OnBtnVcdel)ON_BN_CLICKED(IDC_BTN_VCQRY, OnBtnVcqry)ON_BN_CLICKED(IDC_BTN_VCUPD, OnBtnVcupd)//}}AFX_MSG_MAP END_MESSAGE_MAP()/ // CDlgViewCP message handlersvoid CDlgViewCP::RefreshData(LPCTSTR qry) {m_list.DeleteAllItems();m_list.SetRedraw(FALSE);_RecordsetPtr pRst = NULL;IADORecordBinding *picRs = NULL; //Interface Pointer declared.(VC++ Extensions) CCPRs rs;try{_bstr_t strSQL = qry;TESTHR(pRst.CreateInstance(__uuidof(Recordset)));pRst = m_DBCnt->Execute(strSQL, NULL, adCmdText);TESTHR(pRst->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&picRs));TESTHR(picRs->BindToRecordset(&rs));int i = 0;char buf[128];while (!pRst->adoEOF){m_list.InsertItem(0, rs.m_sz_no);m_list.SetItemText(i, 1, rs.m_sz_name);m_list.SetItemText(i, 2, rs.m_sz_kind);sprintf(buf, "%f", rs.m_f_price); m_list.SetItemText(i, 3, buf);m_list.SetItemText(i, 4, rs.m_sz_detail);pRst->MoveNext();}picRs->Release();pRst->Close();}catch(_com_error& e){AfxMessageBox(e.ErrorMessage());m_list.SetRedraw(TRUE); return;} m_list.SetRedraw(TRUE); }BOOL CDlgViewCP::OnInitDialog() {CDialog::OnInitDialog();m_list.InsertColumn(0,"菜譜號");m_list.InsertColumn(1,"菜名");m_list.InsertColumn(2,"種類");m_list.InsertColumn(3,"價格");m_list.InsertColumn(4,"備注");RECT rect;m_list.GetWindowRect(&rect);int wid = rect.right - rect.left;m_list.SetColumnWidth(0,wid/5);m_list.SetColumnWidth(1,wid/5);m_list.SetColumnWidth(2,wid/5);m_list.SetColumnWidth(3,wid/5);m_list.SetColumnWidth(4,wid/5);m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT);return TRUE; // return TRUE unless you set the focus to a control// EXCEPTION: OCX Property Pages should return FALSE }void CDlgViewCP::OnBtnVcadd() {if (!UpdateData())return;_RecordsetPtr pRst = NULL;CCPRs rs;try{TESTHR(pRst.CreateInstance(__uuidof(Recordset)));pRst->Open("CP",_variant_t((IDispatch *) m_DBCnt, true), adOpenKeyset, adLockOptimistic, adCmdTable);strcpy(rs.m_sz_no, m_no);strcpy(rs.m_sz_name,m_name);strcpy(rs.m_sz_kind, m_kind);rs.m_f_price = m_price;strcpy(rs.m_sz_detail, m_detail);COleSafeArray vaFieldlist, vaValuelist;rs.FillFieldsArray(vaFieldlist,vaValuelist);TESTHR(pRst->AddNew(vaFieldlist, vaValuelist));pRst->Close();}catch(_com_error& e){AfxMessageBox(e.ErrorMessage());return;} MessageBox("完成操作!");CString sql;sql.Format("SELECT * FROM CP WHERE CP_NO='%s'",rs.m_sz_no);RefreshData(sql); }void CDlgViewCP::OnBtnVcdel() {if (!UpdateData())return;CString sql_;sql_.Format("DELETE FROM CP WHERE CP_NO='%s'",m_no);_bstr_t sql = sql_;try{m_DBCnt->Execute(sql,NULL,adCmdText);} catch(_com_error& e){AfxMessageBox(e.ErrorMessage());return;} MessageBox("完成操作!"); OnBtnVcqry(); }void CDlgViewCP::OnBtnVcqry() {CString qry;CString col;GetDlgItemText(IDC_EDIT_VCQRY, qry); GetDlgItemText(IDC_COMBO_VCCOL, col); if ((qry.GetLength()==0) || (col.GetLength()==0)){RefreshData("SELECT * FROM CP");} else{CString sql;sql.Format("SELECT * FROM CP WHERE %s='%s'",col, qry);RefreshData(sql);} }void CDlgViewCP::OnBtnVcupd() {if (!UpdateData())return;CString sql_;sql_.Format("SELECT * FROM CP WHERE CP_NO=%s", m_no);_bstr_t sql = sql_;_RecordsetPtr pRst = NULL;CCPRs rs;try{TESTHR(pRst.CreateInstance(__uuidof(Recordset)));pRst->Open(sql,_variant_t((IDispatch *) m_DBCnt, true),adOpenKeyset,adLockOptimistic,adCmdText);strcpy(rs.m_sz_no, m_no);strcpy(rs.m_sz_name,m_name);strcpy(rs.m_sz_kind, m_kind);rs.m_f_price = m_price;strcpy(rs.m_sz_detail, m_detail);COleSafeArray vaFieldlist, vaValuelist;rs.FillFieldsArray(vaFieldlist,vaValuelist);pRst->Update(vaFieldlist, vaValuelist);pRst->Close();}catch(_com_error& e){AfxMessageBox(e.ErrorMessage());return;} MessageBox("完成操作!");OnBtnVcqry(); }void CDlgViewCP::OnClickListCp(NMHDR* pNMHDR, LRESULT* pResult) {int i = m_list.GetSelectionMark();m_no = m_list.GetItemText(i,0);m_name = m_list.GetItemText(i,1);m_kind = m_list.GetItemText(i,2);m_price = (float)atof(m_list.GetItemText(i,3));m_detail = m_list.GetItemText(i,4);UpdateData(FALSE);*pResult = 0; }首先在對話框構(gòu)造函數(shù)初始化各個成員變量;
然后是數(shù)據(jù)交換和消息映射;
RefreshData函數(shù)刷新列表框的數(shù)據(jù)顯示;
對話框初始化函數(shù)中設(shè)置列頭和各列的寬度;
添加是調(diào)用AddNew方法實現(xiàn),
pRst->AddNew(vaFieldlist, vaValuelist)
很多時候也可以執(zhí)行insert into 的sql語句來實現(xiàn);
刪除是通過執(zhí)行SQL來實現(xiàn):
sql_.Format("DELETE FROM CP WHERE CP_NO='%s'",m_no);
查詢是通過執(zhí)行SQL來實現(xiàn):
sql.Format("SELECT * FROM CP WHERE %s='%s'", col, qry);
更新是首先獲取到該條記錄,
sql_.Format("SELECT * FROM CP WHERE CP_NO=%s", m_no);
設(shè)置值以后調(diào)用Update,
pRst->Update(vaFieldlist, vaValuelist);
總結(jié)
以上是生活随笔為你收集整理的VC访问数据库学习总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用VC++实现一个文本文件阅读器
- 下一篇: Access数据库操作软件研究