1. 程式人生 > >VC++下使用ADO操作資料庫的智慧指標_ConnectionPtr、_RecordsetPtr、_CommandPtr的方法

VC++下使用ADO操作資料庫的智慧指標_ConnectionPtr、_RecordsetPtr、_CommandPtr的方法

_ConnectionPtr介面返回一個記錄集或一個空指標。通常使用它來建立一個數據連線或執行一條不返回任何結果的SQL語句,如一個儲存過程。使用 _ConnectionPtr介面返回一個記錄集不是一個好的使用方法。對於要返回記錄的操作通常用_RecordserPtr來實現。而用 _ConnectionPtr操作時要想得到記錄條數得遍歷所有記錄,而用_RecordserPtr時不需要。
_CommandPtr介面返回一個記錄集。它提供了一種簡單的方法來執行返回記錄集的儲存過程和SQL語句。在使用_CommandPtr介面時,你可以利用全域性 _ConnectionPtr介面,也可以在_CommandPtr接口裡直接使用連線串。如果你只執行一次或幾次資料訪問操作,後者是比較好的選擇。但如果你要頻繁訪問資料庫,並要返回很多記錄集,那麼,你應該使用全域性_ConnectionPtr介面建立一個數據連線,然後使用_CommandPtr 介面執行儲存過程和SQL語句。

_RecordsetPtr是一個記錄集物件。與以上兩種物件相比,它對記錄集提供了更多的控制功能,如記錄鎖定,遊標控制等。同_CommandPtr介面一樣,它不一定要使用一個已經建立的資料連線,可以用一個連線串代替連線指標賦給 _RecordsetPtr的connection成員變數,讓它自己建立資料連線。如果你要使用多個記錄集,最好的方法是同Command物件一樣使用已經建立了資料連線的全域性_ConnectionPtr介面,然後使用_RecordsetPtr執行儲存過程和SQL語句。

(1)、引入ADO類

1
2
3
#import "c:\program files\common files\system\ado\msado15.dll" \
    no_namespace \
rename ("EOF", "adoEOF")

(2)、初始化COM

在MFC中可以用AfxOleInit();非MFC環境中用:
CoInitialize(NULL);
CoUnInitialize();

(3)#import 包含後就可以用3個智慧指標了
:_ConnectionPtr、_RecordsetPtr和_CommandPtr

1.連線和關閉資料庫

(1)連線

例子:連線Access資料庫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
AfxOleInit();//初始化
HRESULT hr;
try
{
    //hr = m_pConnection.CreateInstance
("ADODB.Connection");///建立Connection物件
hr = m_pConnection.CreateInstance(_uuidof(Connection));///建立Connection例項
if(SUCCEEDED(hr)){
        m_pConnection->ConnectionTimeout =0;
        hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=db.mdb", "", "", adModeUnknown);//m_pConnection->PutDefaultDatabase ((_bstr_t)"DB");//設定預設資料庫
 
        m_pCommand.CreateInstance(__uuidof(Command));
        m_pCommand->CommandTimeout =5;
        m_pCommand->ActiveConnection = m_pConnection;}}catch(_com_error e)///捕捉異常{
    CString errormessage;
    errormessage.Format("連線資料庫失敗!\r\n錯誤資訊:%s",e.ErrorMessage());
    AfxMessageBox(errormessage);///顯示錯誤資訊}


(2)、關閉

1
2
3
4
5
6
7
8
9
//如果資料庫連線有效
if( m_pConnection->State )
m_pConnection->Close();
m_pConnection = NULL;
 <span style="color: #ff0000;">(3)、設定連線時間
</span>
//設定連線時間-----------------------------------
pConnection->put_ConnectionTimeout(long(5));


2.開啟一個結果集

(1)開啟,首先建立一個_RecordsetPtr例項,然後呼叫Open()得到一條SQL語句的執行結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
_RecordsetPtrm_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset));
 // 在ADO操作中建議語句中要常用try...catch()來捕獲錯誤資訊,
// 因為它有時會經常出現一些意想不到的錯誤。jingzhou xu
try
{
    m_pRecordset->Open("SELECT * FROM DemoTable",// 查詢DemoTable表中所有欄位
        m_pConnection.GetInterfacePtr(),  // 獲取庫接庫的IDispatch指標
        adOpenDynamic,
        adLockOptimistic,
        adCmdText);
}
catch(_com_error *e)
{
    AfxMessageBox(e->ErrorMessage());
}

(2)關閉結果集
m_pRecordset->Close();

3.操作一個結果集

(1)、遍歷(讀取)

a)、用pRecordset->adoEOF來判斷資料庫指標是否已經移到結果集的末尾了;m_pRecordset->BOF判斷是否 在第一條記錄前面:

1
2
3
4
5
6
7
8
9
10
11
while(!m_pRecordset->adoEOF)
{
    var = m_pRecordset->GetCollect("Name");
    if(var.vt != VT_NULL)
        strName = (LPCSTR)_bstr_t(var);
    var = m_pRecordset->GetCollect("Age");
    if(var.vt != VT_NULL)
        strAge = (LPCSTR)_bstr_t(var);
    m_AccessList.AddString( strName + " --> "+strAge );
    m_pRecordset->MoveNext();
}

b)、取得一個欄位的值的辦法有兩種辦法

一是

//表示取得第0個欄位的值 m_pRecordset->GetCollect(“Name”);

或者 m_pRecordset->GetCollect(_variant_t(long(0));

二是
pRecordset->get_Collect(“COLUMN_NAME”);

或者 pRecordset->get_Collect(long(index));

(2)、新增

a)、呼叫m_pRecordset->AddNew();
b)、呼叫m_pRecordset->PutCollect();給每個欄位賦值
c)、呼叫m_pRecordset->Update();確認

(3)、修改
(4)、刪除

a)、把記錄指標移動到要刪除的記錄上,然後呼叫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Delete(adAffectCurrent) try
{
    // 假設刪除第二條記錄
    m_pRecordset->MoveFirst();
    m_pRecordset->Move(1);
    // 從0開始
    m_pRecordset->Delete(adAffectCurrent);
    // 引數adAffectCurrent為刪除當前記錄
    m_pRecordset->Update();
}
catch(_com_error *e)
{
    AfxMessageBox(e->ErrorMessage());
}

4.直接執行SQL語句,除了要用到結果集其餘的大部分功能都可以直接用SQL語言實現

(1)、用_CommandPtr和_RecordsetPtr配合

1
2
3
4
5
6
7
8
9
_CommandPtrm_pCommand;
 
m_pCommand.CreateInstance(__uuidof(Command));
// 將庫連線賦於它
m_pCommand->ActiveConnection = m_pConnection;
// SQL語句
m_pCommand->CommandText = "SELECT * FROM DemoTable";
// 執行SQL語句,返回記錄集
m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText);


(2)、直接用_ConnectionPtr執行SQL語句

1
2
3
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText,
                                     VARIANT * RecordsAffected,
                                     long Options )

其中CommandText是命令字串,通常是SQL命令。
引數RecordsAffected是操作完成後所影響的行數,
引數Options表示CommandText中內容的型別,Options可以取如下值之一:
adCmdText:表明CommandText是文字命令
adCmdTable:表明CommandText是一個表名
adCmdProc:表明CommandText是一個儲存過程
adCmdUnknown:未知

例子:
_variant_t RecordsAffected;
m_pConnection->Execute(“UPDATE users SET old = old+1″,&RecordsAffected,adCmdText);
5.呼叫儲存過程

(1)、利用_CommandPtr

1
2
3
4
5
_CommandPtrm_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection;  // 將庫連線賦於它
m_pCommand->CommandText = "Demo";
m_pCommand->Execute(NULL,NULL, adCmdStoredProc);

(2)、直接用_ConnectionPtr直接呼叫(見4.(2))

6.遍歷資料庫中的所有表名

_ConnectionPtr m_pConnect;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
_RecordsetPtr pSet;
HRESULT hr;
try
{
    hr = m_pConnect.CreateInstance("ADODB.Connection");
    if(SUCCEEDED(hr))
    {
        CString dd;
        dd.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s",file);
        hr = m_pConnect->Open((_bstr_t)dd,"","",adModeUnknown);
        pSet = m_pConnect->OpenSchema(adSchemaTables);
        while(!(pSet->adoEOF))
        {
            //獲取表格
            _bstr_t table_name = pSet->Fields->GetItem("TABLE_NAME")->Value;
 //獲取表格型別
            _bstr_t table_type = pSet->Fields->GetItem("TABLE_TYPE")->Value;
 //過濾一下,只輸出表格名稱,其他的省略
            if ( strcmp(((LPCSTR)table_type),"TABLE")==0){
                CString tt;
                tt.Format("%s",(LPCSTR)table_name);
                AfxMessageBox(tt);
            }
            pSet->MoveNext();
        }
        pSet->Close();
    }
    m_pConnect->Close();
}catch(_com_error e)///捕捉異常
{
    CString errormessage;
    errormessage.Format("連線資料庫失敗!rn錯誤資訊:%s",e.ErrorMessage());
 
    AfxMessageBox(errormessage);
    return -1;
}


7.遍歷一個表中的所有欄位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Field *   field = NULL;
HRESULT   hr;
Fields *  fields = NULL;
hr = m_pRecordset->get_Fields (&fields);//得到記錄集的欄位集和
 if(SUCCEEDED(hr))
fields->get_Count(&ColCount);
 //得到記錄集的欄位集合中的欄位的總個數
for(i=0;iItem[i]->get_Name(&bstrColName);//得到記錄集//中的欄位名
strColName=bstrColName;
nameField = strColName;
m_FieldsList.AddString(nameField);
}
if(SUCCEEDED(hr))
fields->Release();//釋放指標

附:

1、_variant_t

(1)、一般傳給這3個指標的值都不是MFC直接支援的資料型別,而要用_variant_t轉換一下
_variant_t(XX)可以把大多數型別的變數轉換成適合的型別傳入:
(2)、_variant_t var;_variant_t -> long: (long)var;
_variant_t -> CString: CString strValue = (LPCSTR)_bstr_t(var);
CString -> _variant_t: _variant_t(strSql);
2、BSTR寬字串與CString相互轉換

BSTR bstr;
CString strSql;
CString -> BSTR: bstr = strSql.AllocSysString();
BSTR -> CString: strSql = (LPCSTR)bstr;
3、_bstr_t與CString相互轉換

_bstr_t bstr;
CString strSql;
CString -> _bstr_t: bstr = (_bstr_t)strSql;
_bstr_t -> CString: strSql = (LPCSTR)bstr;
4、關於時間

Access:表示時間的字串#2004-4-5#
Sql:表示時間的字串”2004-4-5”
DateField(時間欄位) select * from my_table where DateField > #2004-4-10#

1
2
3
4
5
6
7
8
9
10
11
try
{
    m_pCommand->CommandText = "INSERT INTO tTest(age) VALUES('23f2') ";
    m_pRecordset = m_pCommand->Execute(NULL,NULL, adCmdText);
}
catch(_com_error e)///捕捉異常
{
    CString errormessage;
    errormessage.Format("連線資料庫失敗!\r\n錯誤資訊:%s",e.ErrorMessage());
    AfxMessageBox(errormessage);///顯示錯誤資訊
}

來源:http://blog.csdn.net/umbrella1984/archive/2005/05/29/383628.aspx

_RecordsetPtr m_pRecordset;//建立一個_RecordsetPtr m_pRecordset.CreateInstance("ADODB.Recordset"); //建立一個例項 try {      m_pRecordset->Open(

"SELECT * FROM duty",                                               //sql查詢語句
   m_pConnection.GetInterfacePtr(),                               //得到sql連線的指標

//Const adOpenDynamic = 2 '動態遊標功能最強,但耗資源也最多。使用者對記錄說做的修改,增加或刪除記錄都將反映到記錄集中。支援全功能瀏覽(ACCESS不支援)。   
   adOpenDynamic,   

//Const adLockOptimistic = 3 '只有在呼叫Update方法時才鎖定記錄集,而在此前的其他操作仍可對當前記錄進行更改、插入和刪除 等                                                      

   adLockOptimistic,

//AdCmdText 將 CommandText 作為命令或儲存過程呼叫的文字化定義進行計算。
   adCmdText); 
}
catch(_com_error e)
{
   //cout<<e->ErrorMessage()<<endl;
   AfxMessageBox("Create Instance failed!");
   return;
}

內容一:

'定義資料庫連線的一些常量 
Const adOpenForwardOnly = 0 '(預設值)遊標只向前瀏覽記錄,不支援分頁、Recordset、BookMark 
Const adOpenKeyset = 1 '鍵集遊標,其他使用者對記錄說做的修改將反映到記錄集中,但其他使用者增加或刪除記錄不會反映到記錄集中。支援分頁、Recordset、BookMark 
Const adOpenDynamic = 2 '動態遊標功能最強,但耗資源也最多。使用者對記錄說做的修改,增加或刪除記錄都將反映到記錄集中。支援全功能瀏覽(ACCESS不支援)。 
Const adOpenStatic = 3 '靜態遊標,只是資料的一個快照,使用者對記錄說做的修改,增加或刪除記錄都不會反映到記錄集中。支援向前或向後移動 

Const adLockReadOnly = 1 '(預設值)鎖定型別,預設的,只讀,不能作任何修改 
Const adLockPessimistic = 2 '當編輯時立即鎖定記錄,最安全的方式 
Const adLockOptimistic = 3 '只有在呼叫Update方法時才鎖定記錄集,而在此前的其他操作仍可對當前記錄進行更改、插入和刪除等 
Const adLockBatchOptimistic = 4 '當編輯時記錄不會被鎖定,而更改、插入和刪除是在批處理方式下完成的 
Const adCmdText = &H0001 
Const adCmdTable = &H0002 
%> 
Open 方法 (ADO Recordset) 
開啟遊標。 
語法 
recordset.Open Source, ActiveConnection, CursorType, LockType, Options

內容二:

CommandType 屬性 
指示 Command 物件的型別。 
設定和返回值 
設定或返回以下某個 CommandTypeEnum 值。 

常量 說明 
AdCmdText 將 CommandText 作為命令或儲存過程呼叫的文字化定義進行計算。 
AdCmdTable 將 CommandText 作為其列全部由內部生成的 SQL 查詢返回的表格的名稱進行計算。 
AdCmdTableDirect 將 CommandText 作為其列全部返回的表格的名稱進行計算。 
AdCmdStoredProc 將 CommandText 作為儲存過程名進行計算。 
AdCmdUnknown 預設值。CommandText 屬性中的命令型別未知。 
adCmdFile 將 CommandText 作為持久 Recordset 檔名進行計算。 
AdExecuteNoRecords 指示 CommandText 為不返回行的命令或儲存過程(例如,插入資料的命令)。如果檢索任意行,則將丟棄這些行且並不返回。它總是與 adCmdText 或 adCmdStoredProc 進行組合。 

說明 
使用 CommandType 屬性可優化 CommandText 屬性的計算。 

如果 CommandType 屬性的值等於 adCmdUnknown(預設值),系統的效能將會降低,因為 ADO 必須呼叫提供者以確定 CommandText 屬性是 SQL 語句、還是儲存過程或表格名稱。如果知道正在使用的命令的型別,可通過設定 CommandType 屬性指令 ADO 直接轉到相關程式碼。如果 CommandType 屬性與 CommandText 屬性中的命令型別不匹配,呼叫 Execute 方法時將產生錯誤。 

adExecuteNoRecords 常量通過最小化內部處理來提高效能。該常量不獨立使用,它總是與 adCmdText 或 adCmdStoredProc 組合(如adCmdText+adExecuteNoRecords) 一起使用。如果與 Recordset.Open 一起使用 adExecuteNoRecords,或者該方法使用 Command 物件都將產生錯誤。

VC++一般注意點(ADO類)

1. 關於ADO智慧指標的釋放問題:
在釋放ADO智慧指標  _ConnectionPtr 和_RecordsetPtr的時候要注意:首先要判斷它們是否是處於Open狀態,是,則先close再判斷指標是否為空,是,則release釋放,否則跳過;
例:
_ConnectionPtr m_pOwnConn;
_RecordsetPtr m_pCmdRs;
……
……
//關閉,釋放智慧指標
//若已經close了再close會崩潰
 if(m_pCmdRs->State==adStateOpen)
 {
  m_pCmdRs->Close();
 }
 if (m_pCmdRs)
 {
  m_pCmdRs.Release();
  m_pCmdRs = NULL;
 }
 
// if(m_pOwnConn->GetStatus())
 if (m_pOwnConn->GetState() == adStateOpen)
 {
  m_pOwnConn->Close();
 }
 if (m_pOwnConn)
 {
  m_pOwnConn.Release();
  m_pOwnConn = NULL;
 }
注:
1.1 m_pConnection物件還沒有產生,就使用其成員state,必然會產生非法訪問錯誤
1.2 _ConnectionPtr   m_pConneciton;  
  if(m_pConnection->state)//如果關閉了是0,開啟是1  
  ///////////////////////////////////////////////////////  
  上敘述方法是不行的。我也遇到過這樣的問題,而且這樣處理的。但測試的時候發現,一但連線上資料庫後,把網線拔了也就是說讓他和資料庫的連線中斷了,跟蹤state的狀態一直為1。我也沒能找到原因。  
  /////////////////////////////////////////////////////////  
  我的解決辦法是用了一個替代辦法,在進行任何操作之前,我去檢索一個肯定存在在資料庫中的表,如果操作成功,說明連線正常。否則從新連線,該程式到現在執行一直很正常。
1.3 用異常處理,捕獲連線斷開的錯誤程式碼。可以判斷連線是否斷開。
2. 關於”_CommandPtr無效指標”
在編譯機(winxp sp2 +office2003 + vc6sp6)上執行程式沒有問題,而在測試機(win2000 +office2000?)上招待程式出現上面說的異常.
程式裡匯入ADO庫如下:
//#import  "C:\Program Files\Common Files\System\ADO\msado15.dll" named_guids rename("EOF","adoEOF"), rename("BOF","adoBOF")

測試機WIN2000,ado的版本是2.5的,而編譯機上的卻是2.8版本的
所以在編譯機上把2.8版本的匯入後,_CommandPtr用的是新版本的介面,舊版本2.5的就無法認出了.
解決方法
1).把一個版本足夠低的ADO版本比如說msado15.dll是2.5XX版本的,放到編譯機上,和程式一起編譯.
2).在測試機上裝MDAC
3. 關於”_CommandPtr”指標建立失敗的原因
1) 沒在標頭檔案加入ADO動態庫,例:
#pragma  warning(disable:4146)
#import "c:\Program Files\Common Files\system\ado\msadox.dll"
#import  "C:\Program Files\Common Files\System\ADO\msado15.dll" named_guids rename("EOF","adoEOF"), rename("BOF","adoBOF")
#pragma  warning(default:4146)
using namespace ADODB;//如果加了這句,就不用在程式碼中再加空間域名字首了

2) 由於引用的ADO庫,是COM庫操作,所以得先初始化COM操作.
 //每個用動COM元件的都要初始化COM庫和解除安裝COM庫
 HRESULT hr = CoInitialize( NULL);
 if ( FAILED( hr ) )
 {
  …
   }
  ::CoUninitialize();

Reference:學習ADO總結