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

CMYODBC:輕量級 ODBC 封裝類,支援動態綁定與事務處理

     編輯:關於VC++

動機:CRecordset 的痛點

  • 需為每個查詢衍生新類,或使用動態資料交換 (DDX/DDV)
  • CRecordset::forwardOnly 游標無法取得欄位數
  • 曾嘗試捕捉 GetFieldValue() 例外來反推欄位數 —— 絕非良策

作者自製 CMYODBC + CODBCSet,代碼精簡 (<2000 行),核心優勢:**查詢結果動態綁定**,無需預知 Schema。

ODBC API 流程對照

  1. SQLAllocHandle(SQL_HANDLE_ENV) → 環境初始化
  2. SQLAllocHandle(SQL_HANDLE_DBC) → 連線控制代碼
  3. SQLConnect / SQLDriverConnect → 連線資料源
  4. SQLAllocHandle(SQL_HANDLE_STMT) → 陳述式控制代碼
  5. SQLExecDirect / SQLPrepare+SQLExecute → 執行 SQL
  6. SQLNumResultCols → 取得欄位數 (關鍵!)
  7. SQLColAttribute / SQLDescribeCol → 取得欄位名、型別、長度
  8. 依型別動態配置記憶體緩衝區
  9. SQLBindCol → 綁定緩衝區至欄位
  10. SQLFetch → 逐列擷取資料
  11. 事務:SQLSetConnectAttr(SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF) → SQLEndTran(SQL_COMMIT/ROLLBACK)
  12. 釋放:SQLFreeHandle (STMT/DBC/ENV)

CMYODBC 公開介面

class CMYODBC {
public:
    BOOL ConnectDB(const char* cpServerName, const char* cpUserName, const char* cpPassword);
    BOOL ExeSqlDirect(const char* cpSqlStmt);           // INSERT/UPDATE/DELETE
    BOOL ExecTrans(CStringList& strSqlList);            // 事務批次執行
    BOOL PrepareSql(const char* cpSql, CODBCSet& rset); // SELECT 預備 (動態綁定)
    BOOL FetchData();                                   // 擷取下一列
    void DisConnect();
    // ... 內部封裝 HENV, HDBC, HSTMT 等控制代碼
};

CODBCSet 動態綁定核心結構

struct COL_DATAFMT_ODBC { // 欄位中繼資料
    char name[50];        // 欄位名
    SWORD type;           // SQL 資料型別
    UDWORD len;           // 定義長度
    SWORD scale;          // 小數位數
    // ...
};
struct COL_DATA_ODBC {    // 單列資料緩衝區
    char value[256];      // 字串化後的值
    SDWORD len;           // 實際長度
};

class CODBCSet {
public:
    int GetCols() const;                  // 欄位數 (SQLNumResultCols 結果)
    COL_DATAFMT_ODBC m_coldatafmt[100];   // 最大 100 欄位
    COL_DATA_ODBC m_coldata[100];         // 當前列資料
    void Empty();                         // 清理緩衝區
    // ... 內部由 CMYODBC::PrepareSql 填充
};

完整使用範例:查詢並顯示於 CListCtrl

BOOL CDemo1Dlg::ShowData() {
    m_list.DeleteAllItems();
    // 清除舊欄位 (需分兩次迴圈,合併會失敗)
    for (int i=0; i<m_list.GetHeaderCtrl()->GetItemCount(); i++)
        m_list.DeleteColumn(0);
    
    CString strSql = _T("select * from emp");
    CMyODBC db; CODBCSet set;
    db.ConnectDB("daliu", "", ""); // Access 無帳密
    db.PrepareSql(strSql, set);     // 關鍵:動態綁定發生在此
    
    // 動態建立欄位
    for (int i=0; i<set.GetCols(); i++)
        m_list.InsertColumn(i, set.m_coldatafmt[i].name, LVCFMT_CENTER, 80);
    
    // 擷取資料
    int iRow = 0;
    while (db.FetchData()) {
        for (int i=0; i<set.GetCols(); i++) {
            if (i==0) m_list.InsertItem(iRow, set.m_coldata[0].value);
            else m_list.SetItemText(iRow, i, set.m_coldata[i].value);
        }
        iRow++;
    }
    set.Empty(); db.DisConnect();
    return TRUE;
}

插入/更新/刪除範例

void CDemo1Dlg::OnBtnadd() {
    UpdateData(TRUE);
    CString strSql;
    strSql.Format(_T("insert into emp values(%d,'%s','%s')"), 
        _ttoi(m_strID), m_strName, m_strJob);
    CMyODBC db;
    db.ConnectDB("daliu", "", "");
    db.ExeSqlDirect(strSql);
    db.DisConnect();
    ShowData(); // 重新整理
}

事務處理範例

CStringList sqlList;
sqlList.AddTail(_T("insert into emp values(100,'A','Job1')"));
sqlList.AddTail(_T("update dept set dname='NEW' where deptno=10"));
sqlList.AddTail(_T("delete from bonus where empno=999"));
CMyODBC db;
db.ConnectDB("daliu", "", "");
db.ExecTrans(sqlList); // 全部成功 Commit,任一失敗 Rollback
db.DisConnect();

自動建立系統 DSN (Access)

// InitInstance 中
char path[MAX_PATH]; GetModuleFileName(NULL, path, MAX_PATH);
CString exePath = path; exePath = exePath.Left(exePath.ReverseFind('\\')+1);
CString mdbPath = exePath + _T("test.mdb");

char cpConfig[MAX_PATH];
strcpy(cpConfig, "DSN=daliu\0");
strcpy(cpConfig+10, "DBQ="); strcpy(cpConfig+14, mdbPath);
strcpy(cpConfig+14+mdbPath.GetLength(), "\0");
strcpy(cpConfig+15+mdbPath.GetLength(), "DEFAULTDIR=");
strcpy(cpConfig+26+mdbPath.GetLength(), exePath);
strcpy(cpConfig+26+mdbPath.GetLength()+exePath.GetLength(), "\0\0");

SQLConfigDataSource(NULL, ODBC_ADD_SYS_DSN,
    "Microsoft Access Driver (*.mdb)\0", cpConfig);

關鍵:設定字串以 \0 分隔屬性,結尾需雙 \0\0。

優缺點總結

優點缺點
零 Schema 依賴,任意 SQL 即查即用無 ORM 對應,手動拼 SQL 易注入
動態綁定自動處理型別/長度大量資料時緩衝區固定 256 bytes 可能截斷
支援事務、預存程序、多種資料源非執行緒安全,連線物件不可跨執行緒共用
代碼極精簡,易閱讀修改缺乏連線池、參數化查詢、非同步支援

適合中小型專案、工具程式、快速原型開發;企業級系統建議改用 ADO/ADO.NET/Entity Framework 等成熟框架。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved