ADO(ActiveX Data Objects)作為 Microsoft 推薦的下一代資料存取介面,以 COM 為基礎、封裝 OLE DB、支援多語言與腳本,極大簡化了資料庫程式開發。本文詳細說明 VC++ 中引入 ADO 型別庫、初始化 COM 環境、三大核心介面(_ConnectionPtr、_CommandPtr、_RecordsetPtr)的選擇策略,並提供連接字串、SQL 執行、斷點續傳等實戰範例。
在 stdafx.h 最前端加入:
#define INITGUID
#import "C:\\Program Files\\Common Files\\System\\ado\\msado15.dll" \
no_namespace rename("EOF", "EndOfFile")
#include <icrsint.h>
no_namespace:避免 using namespace ADODB;,簡化程式碼。rename("EOF", "EndOfFile"):避免與 C++ 標準庫 EOF 衝突。msado15.dll(ADO 2.5/2.6/2.7 通用)。
// 程式啟動時(如 CWinApp::InitInstance)
::CoInitialize(NULL); // 或 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)
// 程式結束時(如 CWinApp::ExitInstance)
::CoUninitialize();
若遺漏 CoInitialize,所有 ADO 呼叫將拋出 CO_E_NOTINITIALIZED 錯誤。
| 介面 | 適用場景 | 特點 |
|---|---|---|
_ConnectionPtr | 建立長連線、執行無傳回結果的 SQL(DDL、存儲過程)、交易控制 | 連線池管理、交易支援 |
_CommandPtr | 參數化查詢、預存程序、需重複執行的 SQL | 參數集合、Prepared Statement |
_RecordsetPtr | 單次查詢、游標操作、鎖定控制、資料導航 | 功能最豐富、可獨立建立連線 |
_ConnectionPtr pConn;
pConn.CreateInstance(__uuidof(Connection));
pConn->Open("DSN=MyDSN;UID=sa;PWD=123", "", "", adConnectUnspecified);
// SQL Server (SQLOLEDB Provider)
pConn->Open("Provider=SQLOLEDB;Server=.;Database=TestDB;UID=sa;PWD=123", "", "", adConnectUnspecified);
// Access (Jet OLEDB)
pConn->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=db.mdb;", "", "", adConnectUnspecified);
// Access 2007+ (ACE OLEDB)
pConn->Open("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=db.accdb;", "", "", adConnectUnspecified);
_RecordsetPtr pRs;
pRs.CreateInstance(__uuidof(Recordset));
pRs->Open("SELECT * FROM Employees WHERE DeptID = 10",
"Provider=SQLOLEDB;Server=.;Database=HR;UID=sa;PWD=123",
adOpenStatic, adLockOptimistic, adCmdText);
| 游標類型 | 特點 | 適用場景 |
|---|---|---|
adOpenForwardOnly | 唯前向、唯讀、最快 | 報表列印、單向掃描 |
adOpenStatic | 靜態快照、可捲動、不見他人變更 | 離線分析、報表 |
adOpenKeyset | 鍵集游標、見他人修改、不見新增/刪除 | 一般編輯介面 |
adOpenDynamic | 動態、見所有變更、支援書籤 | 高併發即時協作 |
| 鎖定類型 | 說明 |
|---|---|
adLockReadOnly | 唯讀(預設) |
adLockOptimistic | 樂觀鎖定,更新時檢查衝突 |
adLockPessimistic | 悲觀鎖定,編輯即鎖定 |
adLockBatchOptimistic | 批次更新模式,配合 UpdateBatch |
// 依欄位名稱存取(推薦)
_variant_t vName = pRs->GetCollect("Name"); // 回傳 _variant_t
CString strName = (LPCTSTR)(_bstr_t)vName; // 轉 CString
// 依索引存取(較快)
_variant_t v = pRs->Fields->GetItem(0)->Value;
// 導航
pRs->MoveFirst();
pRs->MoveNext();
pRs->MovePrevious();
pRs->MoveLast();
if (pRs->EndOfFile) { /* 結束 */ }
// 新增
pRs->AddNew();
pRs->PutCollect("Name", _variant_t("New Employee"));
pRs->PutCollect("Salary", _variant_t(50000));
pRs->Update();
// 修改
pRs->Edit();
pRs->PutCollect("Salary", _variant_t(55000));
pRs->Update();
// 刪除
pRs->Delete(adAffectCurrent);
_CommandPtr pCmd;
pCmd.CreateInstance(__uuidof(Command));
pCmd->ActiveConnection = pConn;
pCmd->CommandText = "SELECT * FROM Users WHERE UserID = ? AND Status = ?";
pCmd->CommandType = adCmdText;
// 附加參數(順序必須與 SQL 佔位符一致)
pCmd->Parameters->Append(pCmd->CreateParameter("UserID", adInteger, adParamInput, 4, 1001));
pCmd->Parameters->Append(pCmd->CreateParameter("Status", adVarChar, adParamInput, 20, "Active"));
_RecordsetPtr pRs = pCmd->Execute(NULL, NULL, adCmdText);
// 讀取長二進位欄位
long offset = 0, chunkSize = 8192;
CFile file("output.dat", CFile::modeCreate | CFile::modeWrite);
while (!pRs->EndOfFile) {
_variant_t chunk = pRs->GetCollect("Photo").GetChunk(chunkSize);
long actualLen = V_ARRAY(&chunk)->rgsabound[0].cElements;
if (actualLen == 0) break;
file.Write(V_ARRAY(&chunk)->pvData, actualLen);
offset += actualLen;
}
file.Close();
pConn->BeginTrans();
try {
pConn->Execute("INSERT ...", NULL, adExecuteNoRecords);
pConn->Execute("UPDATE ...", NULL, adExecuteNoRecords);
pConn->CommitTrans();
} catch (_com_error &e) {
pConn->RollbackTrans();
AfxMessageBox(e.Description());
}
try {
// ADO 操作
} catch (_com_error &e) {
CString msg;
msg.Format(_T("ADO 錯誤: 0x%08X - %s"), e.Error(), e.ErrorMessage());
AfxMessageBox(msg);
// 可進一步檢查 pConn->Errors 集合
}
ADO 以 COM 封裝 OLE DB,提供 _ConnectionPtr/_CommandPtr/_RecordsetPtr 三大智慧指標介面,配合 #import 型別庫自動產生的包裝類別,使 VC++ 資料庫程式碼簡潔、型別安全、部署彈性(DSN-less)。掌握連接字串、游標/鎖定選擇、參數化查詢與交易控制,即可應對企業級資料庫應用開發。