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

C++ 連接 SQL Server 完整實戰指南:ADO、ODBC 設定、CRUD 操作與錯誤處理

     編輯:C++ 入門

C++ 存取 SQL Server 是企業級應用開發的核心技能。本文提供從系統環境設定、ADO 智慧指標初始化、連線建立、SQL 執行、結果集處理到完整 CRUD 範例的端到端實戰指南,基於 Visual C++ (MSVC) + ADO (ActiveX Data Objects) 技術棧,適用於 Windows 平台原生 C++ 專案。

一、系統環境前置作業

1. SQL Server 設定

  • 驗證模式:**SQL Server 和 Windows 驗證模式** (混合模式)。
  • sa 帳號:**啟用** 且 **設定強密碼** (空密碼會被安全策略阻擋)。
  • TCP/IP 協定:**啟用** (SQL Server Configuration Manager → Network Configuration)。
  • 防火牆:開放埠 **1433** (預設執行個體) 或動態埠 (具名執行個體)。

2. ODBC 資料來源設定 (選用,ADO 可直連)

  • 控制台 → 系統管理工具 → **ODBC 資料來源 (64-bit/32-bit 依專案位元數)**。
  • 使用者 DSN / 系統 DSN → **新增** → **ODBC Driver 17/18 for SQL Server**。
  • 驗證方式:**使用 SQL Server 驗證**,輸入 sa 及密碼。
  • 測試連線成功後完成。

二、專案設定與 ADO 智慧指標引入

stdafx.h / pch.h 預編譯標頭引入 ADO


#import "C:\Program Files\Common Files\System\ado\msado15.dll" \
    no_namespace \
    rename("EOF", "adoEOF") \
    rename("BOF", "adoBOF")
  • no_namespace:直接使用 ADO 型別 (如 _ConnectionPtr),無需 ADODB:: 前綴。
  • rename:避免 EOF/BOF 與 C++ 標準庫/巨集衝突。
  • 路徑視 Office/MDAC 安裝位置調整,64 位元系統可能在 C:\Program Files (x86)\...。

核心智慧指標型別

這些型別源自 _com_ptr_t 模板,自動管理 COM 參考計數 (AddRef/Release),離開作用域自動釋放。

三、建立資料庫連線


#include  // _bstr_t, _com_error
#include  // CComPtr 替代方案

void ConnectDatabase() {
    HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); // 初始化 COM 庫
    if (FAILED(hr)) return;

    _ConnectionPtr pConn = nullptr;
    hr = pConn.CreateInstance(__uuidof(Connection));
    if (FAILED(hr)) { CoUninitialize(); return; }

    try {
        // 連線字串 (SQLOLEDB / SQLNCLI11 / MSOLEDBSQL)
        _bstr_t strConn = "Provider=SQLOLEDB.1;"
                          "Server=localhost;"           // 或 IP, 執行個體名
                          "Database=MyDatabase;"
                          "UID=sa;PWD=YourStrongPassword;";

        pConn->Open(strConn, "", "", adConnectUnspecified);
        // 連線成功,pConn 可用於後續操作
    }
    catch (_com_error& e) {
        // 錯誤處理
        MessageBox(nullptr, (LPCTSTR)e.Description(), L"資料庫連線失敗", MB_ICONERROR);
    }
    // 使用完畢後:pConn->Close(); pConn = nullptr; CoUninitialize();
}

連線字串關鍵參數

  • Provider:SQLOLEDB (舊)、SQLNCLI11 (Native Client 11)、MSOLEDBSQL (最新 Microsoft OLE DB Driver for SQL Server,**推薦**)。
  • Server:localhost、192.168.1.100、ServerName\InstanceName。
  • Trusted_Connection=yes:使用 Windows 整合驗證 (免帳密)。
  • Encrypt=yes;TrustServerCertificate=yes:強制加密連線 (Azure SQL/新版驅動程式需求)。

四、執行 SQL 與結果集處理

方法一:Connection::Execute (簡單、無參數化)


_bstr_t sql = "SELECT Id, Name, Price FROM Products WHERE Price > 100";
_RecordsetPtr pRs = pConn->Execute(sql, nullptr, adCmdText);

while (!pRs->adoEOF) {
    // 取得欄位值 (三種方式)
    // 1. 索引存取 (0-based)
    _variant_t vId = pRs->GetCollect(0L);
    // 2. 名稱存取
    _variant_t vName = pRs->Fields->GetItem("Name")->Value;
    // 3. 索引存取 Fields 集合
    _variant_t vPrice = pRs->Fields->GetItem(2L)->Value;

    // 型別轉換 (_variant_t 多型轉換運算子)
    long id = vId.lVal;                    // VT_I4
    CString name = (LPCTSTR)(_bstr_t)vName; // VT_BSTR -> CString
    double price = vPrice.dblVal;           // VT_R8

    // 處理資料...
    pRs->MoveNext();
}
pRs->Close();

方法二:Recordset::Open (支援游標/鎖定類型、可更新)


_RecordsetPtr pRs;
pRs.CreateInstance(__uuidof(Recordset));
pRs->Open(_variant_t("SELECT * FROM Products"),
          _variant_t((IDispatch*)pConn), // 連線物件
          adOpenKeyset,      // 游標類型:可捲動、可見他人變更
          adLockOptimistic,  // 鎖定類型:樂觀鎖定 (更新時檢查)
          adCmdText);        // 命令類型:SQL 文字

// 更新範例
if (!pRs->adoEOF) {
    pRs->Fields->GetItem("Price")->Value = _variant_t(99.9);
    pRs->Update(); // 寫回資料庫
}

游標類型選擇指南

型別封裝介面用途
_ConnectionPtrConnection資料庫連線
_RecordsetPtrRecordset結果集/資料列集合
_CommandPtrCommand參數化命令/預存程序
_ParameterPtrParameter命令參數
常數特性適用場景
adOpenForwardOnly唯讀、僅向前、最快、預設大量資料單向讀取、報表產生
adOpenKeyset可捲動、可見他人修改、不可見新增/刪除一般互動式應用、需更新
adOpenDynamic完全動態、可見所有變更、開銷最大高併發即時協作
adOpenStatic靜態快照、可捲動、不可更新報表、分頁顯示、離線分析

五、完整 CRUD 範例 (參數化命令 — 防 SQL Injection)


void InsertProduct(const CString& name, double price, int stock) {
    _CommandPtr pCmd;
    pCmd.CreateInstance(__uuidof(Command));
    pCmd->ActiveConnection = pConn;
    pCmd->CommandText = "INSERT INTO Products (Name, Price, Stock) VALUES (?, ?, ?)";
    pCmd->CommandType = adCmdText;

    // 參數順序對應 ?
    pCmd->Parameters->Append(pCmd->CreateParameter("Name", adVarWChar, adParamInput, 100, (LPCTSTR)name));
    pCmd->Parameters->Append(pCmd->CreateParameter("Price", adCurrency, adParamInput, 0, price));
    pCmd->Parameters->Append(pCmd->CreateParameter("Stock", adInteger, adParamInput, 0, stock));

    pCmd->Execute(nullptr, nullptr, adExecuteNoRecords);
}

void UpdateProduct(int id, double newPrice) {
    _CommandPtr pCmd;
    pCmd.CreateInstance(__uuidof(Command));
    pCmd->ActiveConnection = pConn;
    pCmd->CommandText = "UPDATE Products SET Price = ? WHERE Id = ?";
    pCmd->CommandType = adCmdText;
    pCmd->Parameters->Append(pCmd->CreateParameter("Price", adCurrency, adParamInput, 0, newPrice));
    pCmd->Parameters->Append(pCmd->CreateParameter("Id", adInteger, adParamInput, 0, id));
    pCmd->Execute(nullptr, nullptr, adExecuteNoRecords);
}

void DeleteProduct(int id) {
    _CommandPtr pCmd;
    pCmd.CreateInstance(__uuidof(Command));
    pCmd->ActiveConnection = pConn;
    pCmd->CommandText = "DELETE FROM Products WHERE Id = ?";
    pCmd->CommandType = adCmdText;
    pCmd->Parameters->Append(pCmd->CreateParameter("Id", adInteger, adParamInput, 0, id));
    pCmd->Execute(nullptr, nullptr, adExecuteNoRecords);
}

六、錯誤處理與資源管理最佳實踐

統一錯誤處理巨集/函數


#define CHECK_HR(hr, msg) \
    if (FAILED(hr)) { \
        _com_issue_error(hr); \
    }

#define CATCH_COM_ERROR \
    catch (_com_error& e) { \
        CString errMsg; \
        errMsg.Format(L"%s\nHRESULT: 0x%08X\nDescription: %s", \
                      msg, e.Error(), (LPCTSTR)e.Description()); \
        AfxMessageBox(errMsg, MB_ICONERROR); \
    } \
    catch (...) { \
        AfxMessageBox(L"未知嚴重錯誤", MB_ICONERROR); \
    }

RAII 封裝連線/記錄集 (現代 C++ 風格)


class AdoConnection {
    _ConnectionPtr conn_;
public:
    AdoConnection() = default;
    ~AdoConnection() { if (conn_) { conn_->Close(); conn_ = nullptr; } }
    bool Open(const _bstr_t& connStr) {
        HRESULT hr = conn_.CreateInstance(__uuidof(Connection));
        if (FAILED(hr)) return false;
        try { conn_->Open(connStr, "", "", adConnectUnspecified); return true; }
        catch (...) { return false; }
    }
    _ConnectionPtr Get() const { return conn_; }
    operator bool() const { return conn_ != nullptr; }
};

class AdoRecordset {
    _RecordsetPtr rs_;
public:
    AdoRecordset() = default;
    ~AdoRecordset() { if (rs_) { rs_->Close(); rs_ = nullptr; } }
    bool Open(_ConnectionPtr conn, const _bstr_t& sql, long cursor=adOpenForwardOnly, long lock=adLockReadOnly) {
        HRESULT hr = rs_.CreateInstance(__uuidof(Recordset));
        if (FAILED(hr)) return false;
        try { rs_->Open(_variant_t(sql), _variant_t((IDispatch*)conn), cursor, lock, adCmdText); return true; }
        catch (...) { return false; }
    }
    _RecordsetPtr Get() const { return rs_; }
    bool Next() { if (rs_ && !rs_->adoEOF) { rs_->MoveNext(); return !rs_->adoEOF; } return false; }
};

七、效能優化與避坑指南

  • 連線池:ADO/OLE DB 預設啟用連線池,相同連線字串重複 Open/Close 會複用實體連線。ConnectionString 必須**完全一致** (含空白、大小寫)。
  • 參數化命令:務必使用 _CommandPtr + 參數,防止 SQL Injection、提升執行計畫重用率。
  • 批次操作:大量寫入用 adExecuteNoRecords、關閉自動認可 (pConn->BeginTrans() / CommitTrans()) 或使用 bcp/SqlBulkCopy (需 .NET/ODBC)。
  • 資料型別對應:_variant_t 轉換需注意 VT_NULL (資料庫 NULL) vs VT_EMPTY,使用 v.vt == VT_NULL 判斷 NULL 值。
  • 64 位元專案:確保安裝 64 位元 OLE DB Driver (MSOLEDBSQL),#import 路徑指向 64 位元 DLL。

八、替代方案現代化選擇

掌握 ADO 核心流程與現代化替代方案,能讓你在 Windows C++ 專案中靈活、安全地處理 SQL Server 資料存取需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方案優點缺點適用場景
ADO (OLE DB)Windows 原生、COM 整合、支援舊版 SQLCOM 複雜、OLE DB 已棄用 (Microsoft 建議改用 ODBC)既有 MFC/ATL 專案維護
ODBC (C API)跨平台、標準、Microsoft 推薦、效能佳C 風格 API 繁瑣、需手動管理控制代碼新專案、跨平台、高效能需求
ODBC + 封裝庫如 cpp-db、ODBCpp、sqlpp11 (C++11 型別安全)第三方依賴現代 C++ 專案首選
Entity Framework Core (C++/CLI)ORM、LINQ、遷移需 .NET Runtime、C++/CLI 混合模式.NET 生態整合專案