AppWizard 生成的數據庫應用會自動創建 CRecordset 派生類並綁定特定表/視圖,雖然方便但缺乏靈活性:無法動態構建 SQL、難以跨表查詢、調試時綁定邏輯不透明。直接使用基類 CRecordset 可執行任意 SQL、動態綁定列、靈活控制游標類型,適合通用數據訪問層、報表引擎、動態查詢工具等場景。
// stdafx.h 添加數據庫支持頭文件
#include // MFC 數據庫類
// 確保項目設置:MFC 使用共享 DLL / 靜態庫均可
// 鏈接庫:自動鏈接 nafxcwd.lib / mfcsxxd.lib 等
| 類/函數 | 用途 |
|---|---|
CDatabase | 管理數據源連接(ODBC),執行 OpenEx/Open 連接 DSN 或無 DSN 連接字符串 |
CRecordset::Open | 執行 SQL 查詢/存儲過程,參數:游標類型、SQL 字符串、選項標誌 |
MoveFirst/Next/Prev/Last | 游標導航 |
IsBOF/IsEOF | 判斷游標邊界 |
GetFieldValue | 按索引/名稱獲取字段值,支持 CString/CDBVariant 等多種重載 |
GetODBCFieldInfo | 獲取字段元數據(類型、長度、精度等) |
class CMyDBManager
{
public:
CDatabase m_dbConn; // 成員變量保持連接存活
BOOL Connect(LPCTSTR lpszDSN = _T("MyDSN"))
{
// 無 DSN 連接字符串示例:
// "DRIVER={SQL Server};SERVER=localhost;DATABASE=TestDB;UID=sa;PWD=123;"
BOOL bOK = m_dbConn.OpenEx(lpszDSN, CDatabase::noOdbcDialog);
TRACE(_T("Connect %s: %d\n"), lpszDSN, bOK);
return bOK;
}
void Disconnect()
{
if (m_dbConn.IsOpen())
m_dbConn.Close();
}
};
void CMyDBManager::QuerySample()
{
if (!m_dbConn.IsOpen()) return;
CRecordset rs(&m_dbConn);
// 參數說明:
// CRecordset::forwardOnly - 僅前向游標,性能最佳,不支持 MovePrev
// CRecordset::snapshot - 快照型,只讀,支持雙向導航
// CRecordset::dynaset - 動態集,可更新,開銷最大
CString strSQL = _T("SELECT loc_id, loc_name, created_dt FROM table1 ORDER BY loc_id");
BOOL bOK = rs.Open(CRecordset::forwardOnly, strSQL, CRecordset::readOnly);
TRACE(_T("Open result: %d, Fields: %d\n"), bOK, rs.GetODBCFieldCount());
if (!bOK) return;
CString strValue;
while (!rs.IsEOF())
{
// 方式 1:按索引獲取 CString(自動類型轉換)
rs.GetFieldValue((short)0, strValue); // loc_id
TRACE(_T("ID: %s "), strValue);
rs.GetFieldValue((short)1, strValue); // loc_name
TRACE(_T("Name: %s "), strValue);
rs.GetFieldValue((short)2, strValue); // created_dt
TRACE(_T("Date: %s\n"), strValue);
rs.MoveNext();
}
rs.Close();
}
void CMyDBManager::QueryWithVariant()
{
CRecordset rs(&m_dbConn);
rs.Open(CRecordset::snapshot, _T("SELECT * FROM table1"), CRecordset::readOnly);
CDBVariant var;
while (!rs.IsEOF())
{
for (short i = 0; i < rs.GetODBCFieldCount(); ++i)
{
rs.GetFieldValue(i, var); // 統一獲取到 CDBVariant
switch (var.m_dwType)
{
case DBVT_NULL:
TRACE(_T("[NULL] "));
break;
case DBVT_BOOL:
TRACE(_T("%s "), var.m_boolVal ? _T("True") : _T("False"));
break;
case DBVT_SHORT:
TRACE(_T("%d "), var.m_iVal);
break;
case DBVT_LONG:
TRACE(_T("%ld "), var.m_lVal);
break;
case DBVT_SINGLE:
TRACE(_T("%.2f "), var.m_fltVal);
break;
case DBVT_DOUBLE:
TRACE(_T("%.4f "), var.m_dblVal);
break;
case DBVT_DATE:
{
CString str = var.m_pdate->Format(_T("%Y-%m-%d %H:%M:%S"));
TRACE(_T("%s "), str);
}
break;
case DBVT_STRING:
case DBVT_ASTRING:
TRACE(_T("%s "), (LPCTSTR)*var.m_pstring);
break;
case DBVT_BINARY:
TRACE(_T("[BINARY:%lu bytes] "), var.m_pbinary->GetSize());
break;
default:
TRACE(_T("[TYPE:%lu] "), var.m_dwType);
}
}
TRACE(_T("\n"));
rs.MoveNext();
}
rs.Close();
}
BOOL CMyDBManager::ExecuteSQL(LPCTSTR lpszSQL)
{
if (!m_dbConn.IsOpen()) return FALSE;
try
{
m_dbConn.ExecuteSQL(lpszSQL);
return TRUE;
}
catch (CDBException* e)
{
TRACE(_T("SQL Error: %s\n"), e->m_strError);
e->Delete();
return FALSE;
}
}
// 使用示例
ExecuteSQL(_T("INSERT INTO table1 (loc_name) VALUES ('New Location')"));
ExecuteSQL(_T("UPDATE table1 SET loc_name='Modified' WHERE loc_id=10"));
ExecuteSQL(_T("DELETE FROM table1 WHERE loc_id > 100"));
| 類型 | 特點 | 適用場景 | 性能 |
|---|---|---|---|
forwardOnly | 單向、只讀、無緩存 | 大數據集順序讀取、報表生成、數據導出 | ★★★★★ 最快 |
snapshot | 靜態快照、雙向、只讀 | 需要隨機訪問的只讀數據、表單綁定 | ★★★☆☆ |
dynaset | 動態、可更新、雙向 | 需編輯數據的表單、雙向導航 | ★★☆☆☆ 開銷大 |
dynamic | 服務端游標、實時反映他人變更 | 高併發協作環境(需驅動程序支持) | ★☆☆☆☆ 視驅動而定 |
try-catch(CDBException*) 中,捕獲後調用 e->Delete() 釋放異常對象。CRecordset::Close() 必須調用,析構函數會自動調用但顯式關閉更安全。CRecordset::Open 參數綁定或 CDatabase::ExecuteSQL 參數化(需 ODBC 3.0+ 驅動支持)。CLongBinary / CByteArray 處理 BLOB/IMAGE/TEXT 字段,需設置 CRecordset::useMultiRowFetch 等選項優化。CDatabase 重複 Open/Close 可複用物理連接。CString 為 wchar_t,ODBC 驅動需支持 Unicode(SQL_WCHAR)。
class CDBAccessor
{
CDatabase& m_db;
public:
CDBAccessor(CDatabase& db) : m_db(db) {}
// 返回 vector
獨立使用 CRecordset 基類打破了向導生成代碼的靈活性限制,配合 CDBVariant 實現類型無關的字段訪問,構建通用數據訪問層成為可能。掌握游標類型選擇、異常處理、資源管理等核心要點,可編寫健壯、高效、可維護的數據庫應用代碼。