首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ ADO 操作 Access 資料庫完整實戰:連接、CRUD、Recordset 遍歷與異常處理

     編輯:關於VC++

前言:ADO 簡介

ADO (ActiveX Data Objects) 建於 OLE DB 之上,提供簡潔、高階的資料存取介面,支援 Access、SQL Server、Oracle 等多種資料源。本文以 Access 2000 (`test.mdb`) 為例,示範 VC++ 中 `#import` 引入 ADO 型別庫、連接資料庫、執行 SQL、Recordset 操作與完整 CRUD 範例。

環境建置

  1. 專案 `stdafx.h` 加入:
#import "C:\\Program Files\\Common Files\\System\\ADO\\msado15.dll" \
    no_namespace rename("EOF", "adoEOF")

※ 路徑視安裝位置調整。編譯會產生 `msado15.tlh/tli`。忽略 `C4146` 警告。

  1. `InitInstance` 初始化 COM:
BOOL CMyApp::InitInstance() {
    AfxOleInit(); // 或 CoInitialize(NULL)
    // ...
}

核心物件與智慧指標

  • `_ConnectionPtr`:連接物件
  • `_RecordsetPtr`:記錄集
  • `_CommandPtr`:命令物件 (預存程序/參數化查詢)
  • `_variant_t`:VARIANT 封裝,支援自動型別轉換
  • `_bstr_t`:BSTR 封裝,字串處理
  • `_com_error`:COM 例外類別

連接資料庫

_ConnectionPtr m_pConn;
HRESULT hr;
try {
    hr = m_pConn.CreateInstance(__uuidof(Connection));
    if (SUCCEEDED(hr)) {
        hr = m_pConn->Open(
            _bstr_t("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb"),
            _bstr_t(""), _bstr_t(""), adModeUnknown);
        // Access 97 用 Provider=Microsoft.Jet.OLEDB.3.51
    }
}
catch (_com_error& e) {
    CString msg; msg.Format(_T("連接失敗: %s"), e.ErrorMessage());
    AfxMessageBox(msg);
}

常用連接字串

  • Access 2000: `Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb`
  • DSN: `DSN=myDSN;UID=sa;PWD=;`
  • SQL Server (無 DSN): `Driver={SQL Server};Server=127.0.0.1;Database=myDB;UID=sa;PWD=123`

執行 SQL 與取得 Recordset 三種方式

1. Connection::Execute (最簡潔)

_variant_t rowsAffected;
// 建表
m_pConn->Execute(_bstr_t("CREATE TABLE Users(ID INT, Name TEXT, Age INT, Birthday DATETIME)"), &rowsAffected, adCmdText);
// 新增
m_pConn->Execute(_bstr_t("INSERT INTO Users VALUES(1, 'John', 25, '1990-1-1')"), &rowsAffected, adCmdText);
// 更新
m_pConn->Execute(_bstr_t("UPDATE Users SET Age = Age + 1"), &rowsAffected, adCmdText);
// 查詢計數
_RecordsetPtr rs = m_pConn->Execute(_bstr_t("SELECT COUNT(*) FROM Users"), &rowsAffected, adCmdText);
_variant_t cnt = rs->GetCollect(_variant_t((long)0));
rs->Close();

2. Command 物件 (適合參數化/預存程序)


_CommandPtr cmd;
cmd.CreateInstance(__uuidof(Command));
cmd->ActiveConnection = m_pConn;
cmd->CommandText = _bstr_t("SELECT * FROM Users");
_RecordsetPtr rs = cmd->Execute(NULL, NULL, adCmdText);

3. Recordset::Open (最靈活,支援游標/鎖定)


_RecordsetPtr rs;
rs.CreateInstance(__uuidof(Recordset));
rs->Open(_bstr_t("SELECT * FROM Users"),
         _variant_t((IDispatch*)m_pConn, true),
         adOpenStatic, adLockOptimistic, adCmdText);

游標類型 (CursorTypeEnum)

常數值特性
adOpenForwardOnly0唯前向、唯讀、高效能
adOpenKeyset1可見他人更新、不可見新增/刪除
adOpenDynamic2即時反映所有變更、支援書籤
adOpenStatic3靜態快照、支援書籤/移動、不見他人變更

鎖定類型 (LockTypeEnum)

常數值特性
adLockReadOnly1唯讀
adLockPessimistic2編輯即鎖定 (悲觀)
adLockOptimistic3Update 時才鎖定 (樂觀,推薦)
adLockBatchOptimistic4批次更新模式

Recordset 遍歷、修改、新增、刪除


_variant_t vID, vName, vAge, vBD;
_RecordsetPtr rs;
rs.CreateInstance(__uuidof(Recordset));
rs->Open(_bstr_t("SELECT * FROM Users"),
         _variant_t((IDispatch*)m_pConn, true),
         adOpenStatic, adLockOptimistic, adCmdText);

// 遍歷
while (!rs->adoEOF) { // 注意:rename("EOF","adoEOF")
    vID = rs->GetCollect(_variant_t((long)0)); // 索引 0
    vName = rs->GetCollect(_bstr_t("Name"));   // 或欄位名
    vAge = rs->GetCollect(_bstr_t("Age"));
    vBD = rs->GetCollect(_bstr_t("Birthday"));
    TRACE(_T("ID:%d Name:%s Age:%d BD:%s\n"),
        vID.lVal, (LPCTSTR)(_bstr_t)vName, vAge.lVal, (LPCTSTR)(_bstr_t)vBD);
    rs->MoveNext();
}

// 刪除第一筆
rs->MoveFirst();
rs->Delete(adAffectCurrent);

// 新增三筆
for (int i=0; i<3; i++) {
    rs->AddNew();
    rs->PutCollect(_bstr_t("ID"), _variant_t(i+10));
    rs->PutCollect(_bstr_t("Name"), _bstr_t("Yeltsin"));
    rs->PutCollect(_bstr_t("Age"), _variant_t(71));
    rs->PutCollect(_bstr_t("Birthday"), _bstr_t("1930-3-15"));
}

// 修改第二筆年齡
rs->Move(1, _variant_t((long)adBookmarkFirst)); // 從第一筆移動 1 格
rs->PutCollect(_bstr_t("Age"), _variant_t(45));
rs->Update(); // 寫回資料庫

rs->Close();

完整範例:MFC Dialog 列表顯示 + 編輯/新增/刪除

本文附帶完整專案 `ADOTest1`,包含:

  • `CListCtrl` 顯示資料 (全列選取、報表模式)
  • 編輯欄位:ID、姓名、年齡、生日 (DateTimePicker)
  • 按鈕:新增、刪除、列表選取同步編輯欄位
  • 自動儲存機制:切換列選取時 `Update()` 前一筆,`LoadData()` 新選取

核心類別成員:


_RecordsetPtr m_pRecordset;
_variant_t vUserID, vUsername, vOld, vBirthday;
int m_nCurrentSel;
BOOL m_bAutoSave, m_bSuccess;

關閉連接與資源釋放


if (m_pRecordset) m_pRecordset->Close();
if (m_pConn && m_pConn->State) m_pConn->Close();
// 智慧指標離開範圍自動 Release()

常見問題與最佳實踐

  1. 欄位名大小寫:Access 不區分,但 `GetCollect` 建議用確切名稱
  2. 日期格式:`_variant_t` 直接支援 `COleDateTime` 互轉
  3. 異常處理:所有 ADO 呼叫包在 `try/catch(_com_error&)`
  4. 連接共用:App 類別持有 `_ConnectionPtr`,Dialog 共用
  5. 資料類型對應:`_variant_t` 建構子支援 `long`、`double`、`LPCTSTR`、`COleDateTime` 等
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved