摘 要:本文在介紹ODBC技術的基礎上,將MFC和ODBC API結合起來創建了兩個自 定義 類,實現了對任意異構型數據庫庫結構信息的獲取。 關鍵字:ODBC,MFC,異構型數據庫,記錄集
一. 問題的提出 隨著數據庫技術在各個應用領域的迅速發展,市場上推出了多種數據庫系 統,為了充分利用資源,實現信息共享,以便用戶能對異構型數據庫實現透明的 訪問(包括數據查詢、更新和交換等功能),作者開發了異構型數據庫通信平臺。 在平臺的研制過程中,獲取各種異構型數據庫的結構信息是進行數據訪問的前 提。作者以VC++5.0為開發語言,利用ODBC實現了這一關鍵技術。
二. ODBC技術介紹 ODBC技術是指開放性數據庫連接技術,該技術使應用程序無需關心數據源來 自何種DBMS,利用其標準接口實現與數據源之間的數據交換。傳統的ODBC編程是 利用高級語言(如C語言)調用ODBC的API來實現。應用程序要求驅動程序管理 器和每個驅動程序為ODBC環境、每個連接以及每個SQL語句分配信息存儲空間, 并返回指向各個存儲區的句柄供其調用。ODBC接口定義了三種句柄類型: 環境句柄:為全程信息標識內存存儲,包括有效連接句柄及當前活動連接句柄。 ODBC將環境句柄定義為HENV類型的變量。應用程序使用單一環境句柄,它必須 在連接到數據源前請求該句柄。 連接句柄:為特定連接的信息標識了內存存儲。ODBC將連接句柄定義為 HDBC類型。應用程序必須在連接到數據源前請求連接句柄。每個連接句柄與環 境句柄有關。然而,環境句柄可以有多個與其有關的連接句柄。 語句句柄:為SQL語句信息標識內存存儲。ODBC將語句句柄定義為HSTMT類型變 量。應用程序必須在提交SQL請求之前請求語句句柄。每個語句句柄與一個連接 句柄有關。然而,每個連接句柄可以有多個與其相關的語句句柄。 下面以C語言為例說明傳統ODBC編程的一般過程。 1、 環境申請,分配環境句柄 HENV henv; SQLAllocEnv(&henv); 說明:分配一個環境句柄,支持一個或多個數據源連接。 2、 連接申請,分配連接句柄 HDBC hdbc; SQLAllocConnect(henv,&hdbc); 說明:一個連接句柄對應一個數據源,可以有多個連接句柄。 3、 連接數據源,用連接句柄連接到數據源 SQLDriverConnect(hdbc,...); 說明:以對話框方式獲取注冊信息,并連接數據源。 4、 語句申請,分配語句句柄 SQLAllocStmt(hdbc,&hstmt); 說明:獲得語句句柄,以便執行SQL語句。 5、 執行SQL語句 SQLExecDirect(hstmt,SQLStatement,..); 說明:利用語句句柄,執行SQL語句。 6、 釋放所有資源 SQLfreeStemt(hstmst,...); //釋放語句句柄 SQLDisconnect(hdbc); //斷開連接 SQLFreeConnect(hdbc); //釋放當前數據庫連接句柄 SQLFreeEnv(henv); //釋放環境句柄
三. 利用VC++和ODBC技術獲取異構型數據庫結構信息 傳統的ODBC編程過程比較復雜,各種參數不易理解,且直接獲取返回的數據較 困難。VC++ 5.0的MFC類庫對ODBC的API進行封裝,部分簡化了ODBC編程(尤其 是對數據庫記錄集的操作),但單純利用MFC類獲取異構型數據庫的結構信息仍 然比較困難,因此需要將MFC和傳統ODBC API編程結合起來。作者利用ODBC接口 函數重載了MFC中CRecordset類的部分成員函數,創建CTable和CColumns類。利 用這兩個新創建的類,可以很方便的獲取異構型數據庫結構信息。 下面就是關于CTable和Ccolumns類的定義: class CTable : public CRecordset { virtual CString GetDefaultConnect() { return ""; } virtual CString GetDefaultSQL() { return ""; } public: CTable(CDatabase* pDatabase); BOOL Open(LPCSTR pszTableQualifier = NULL, LPCSTR pszTableOwner = NULL, LPCSTR pszTableName = NULL, LPCSTR pszTableType = NULL, UINT nOpenType = forwardOnly); CString m_strTableQualifier; CString m_strTableOwner; CString m_strTableName; CString m_strTableType; CString m_strRemarks; virtual void DoFieldExchange(CFieldExchange*); };
class CColumns : public CRecordset { virtual CString GetDefaultConnect() { return ""; } virtual CString GetDefaultSQL() { return ""; } public: CColumns(CDatabase* pDatabase); BOOL Open(LPCSTR pszTableQualifier = NULL, LPCSTR pszTableOwner = NULL, LPCSTR pszTableName = NULL, LPCSTR pszColumnName = NULL, UINT nOpenType = forwardOnly); CString m_strTableQualifier; CString m_strTableOwner; CString m_strTableName; CString m_strColumnName; int m_nDataType; CString m_strTypeName; long m_nPrecision; long m_nLength; int m_nScale; int m_nRadix; int m_fNullable; CString m_strRemarks; virtual void DoFieldExchange(CFieldExchange*); }; BOOL CColumns::Open(LPCSTR pszTableQualifier, LPCSTR pszTableOwner,LPCSTR pszTableName,LPCSTR pszColumnName, UINT nOpenType) { RETCODE nRetCode; UWORD bFunctionExists; //檢驗是否支持SQLColumns函數 AFX_SQL_SYNC(::SQLGetFunctions(m_pDatabase->m_hdbc, SQL_API_SQLCOLUMNS,&bFunctionExists)); if (!Check(nRetCode) || !bFunctionExists) { if (!bFunctionExists) TRACE(_T("SQLColumns 不支持\n")); return FALSE; } //設置緩沖區狀態,分配語句句柄 SetState(nOpenType,NULL,readOnly); if (!AllocHstmt()) return FALSE; TRY { OnSetOptions(m_hstmt); AllocStatusArrays(); // 調用ODBC的SQLColumns函數 AFX_ODBC_CALL(::SQLColumns(m_hstmt, (UCHAR FAR*)pszTableQualifier,SQL_NTS, (UCHAR FAR*)pszTableOwner,SQL_NTS, (UCHAR FAR*)pszTableName,SQL_NTS, (UCHAR FAR*)pszColumnName,SQL_NTS)); if (!Check(nRetCode)) ThrowDBException(nRetCode,m_hstmt); // 分配內存,填寫信息 AllocAndCacheFieldInfo(); AllocRowset(); MoveNext(); m_bBOF = m_bEOF; }
//異常信息的捕獲 CATCH_ALL(e) { Close(); THROW_LAST(); } END_CATCH_ALL return TRUE; } //獲取記錄集信息 void CColumns::DoFieldExchange(CFieldExchange* pFX) { pFX->SetFieldType(CFieldExchange::outputColumn); RFX_Text(pFX,_T("TABLE_QUALIFIER"),m_strTableQualifier); RFX_Text(pFX,_T("TABLE_OWNER"),m_strTableOwner); RFX_Text(pFX,_T("TABLE_NAME"),m_strTableName); RFX_Text(pFX,_T("COLUMN_NAME"),m_strColumnName); RFX_Int(pFX,_T("DATA_TYPE"),m_nDataType); RFX_Text(pFX,_T("TYPE_NAME"),m_strTypeName); RFX_Long(pFX,_T("PRECISION"),m_nPrecision); RFX_Long(pFX,_T("LENGTH"),m_nLength); RFX_Int(pFX,_T("SCALE"),m_nScale); RFX_Int(pFX,_T("RADIX"),m_nRadix); RFX_Int(pFX,_T("NULLABLE"),m_fNullable); RFX_Text(pFX,_T("REMARKS"),m_strRemarks); } CColumns::CColumns(CDatabase* pDatabase): CRecordset(pDatabase) { m_strTableQualifier = _T(""); m_strTableOwner = _T(""); m_strTableName = _T(""); m_strColumnName = _T(""); m_nDataType = 0; m_strTypeName = _T(""); m_nPrecision = 0; m_nLength = 0; m_nScale = 0; m_nRadix = 0; m_fNullable = 0; m_strRemarks = _T(""); m_nFields = 12; }
CTable::CTable(CDatabase* pDatabase): CRecordset(pDatabase) { m_strTableQualifier = _T(""); m_strTableOwner = _T(""); m_strTableName = _T(""); m_strTableType = _T(""); m_strRemarks = _T(""); m_nFields = 5; }
BOOL CTable::Open(LPCSTR pszTableQualifier, LPCSTR pszTableOwner,LPCSTR pszTableName,LPCSTR pszTableType, UINT nOpenType) { RETCODE nRetCode; UWORD bFunctionExists; //檢驗是否支持SQLTables 函數 AFX_SQL_SYNC(::SQLGetFunctions(m_pDatabase->m_hdbc, SQL_API_SQLTABLES,&bFunctionExists)); if (!Check(nRetCode) || !bFunctionExists) { if (!bFunctionExists) TRACE(_T("SQLTables 不支持\n")); return FALSE; } //設置緩沖區狀態,分配語句句柄 SetState(nOpenType,NULL,readOnly); if (!AllocHstmt()) return FALSE; TRY { OnSetOptions(m_hstmt); AllocStatusArrays(); //調用 ODBC的SQLTables函數 AFX_ODBC_CALL(::SQLTables(m_hstmt, (UCHAR FAR*)pszTableQualifier,SQL_NTS, (UCHAR FAR*)pszTableOwner,SQL_NTS, (UCHAR FAR*)pszTableName,SQL_NTS, (UCHAR FAR*)pszTableType,SQL_NTS)); if (!Check(nRetCode)) ThrowDBException(nRetCode,m_hstmt); // 分配內存,填寫信息 AllocAndCacheFieldInfo(); AllocRowset(); MoveNext(); m_bBOF = m_bEOF; } //異常信息的捕獲 CATCH_ALL(e) { Close(); THROW_LAST(); } END_CATCH_ALL return TRUE; } void CTable::DoFieldExchange(CFieldExchange* pFX) { pFX->SetFieldType(CFieldExchange::outputColumn); RFX_Text(pFX,_T("TABLE_QUALIFIER"),m_strTableQualifier); RFX_Text(pFX,_T("TABLE_OWNER"),m_strTableOwner); RFX_Text(pFX,_T("TABLE_NAME"),m_strTableName); RFX_Text(pFX,_T("TABLE_TYPE"),m_strTableType); RFX_Text(pFX,_T("REMARKS"),m_strRemarks); } 以上兩個類對CRecordset的Open和DoFieldExchange函數進行了重載。應用程序 可以在需要時創建CTable或Ccolumns類,并調用OPEN成員函數建立相應的表結構 和字段結構記錄集。接下來就可以通過下列函數來遍歷異構型數據庫的結構信息 了。 Void CRecordset::MoveFirst(); //移到第一條記錄 Void CRecordset::MoveLast(); //移到最后一條記錄 Void CRecordset::MovePrev(); //移到前一條記錄 Void CRecordset::MoveNext(); //移到后一條記錄 BOOL CRecordset::IsBOF(); //判斷是否到達第一條記錄前 BOOL CRecordset::IsEOF(); //判斷是否到達最后一條記錄后
四、結束語 利用自定義的CTable和Ccolumns類,應用程序能獲取任何異構型數據庫庫結構 信息。根據獲得的信息可以方便的對未知數據庫進行相應的操作。若將CTable和 Ccolumns類與文檔類、視類結合起來,就可以在窗口里以一定的方式顯示結構 信息。作者利用以上技術在異構型數據庫通信平臺上成功實現了對各種異構型 數據庫庫結構信息的獲取。
|