利用VC++獲取異構型資料庫庫結構資訊 (轉)

worldblog發表於2007-12-04
利用VC++獲取異構型資料庫庫結構資訊 (轉)[@more@]

利用VC++獲取異構型庫結構資訊


空軍電訊工程學院室 萬映輝 邸曉奕
摘 要:本文在介紹OC技術的基礎上,將MFC和ODBC 結合起來建立了兩個自定義 類,
實現了對任意異構型資料庫庫結構資訊的獲取。
關鍵字:ODBC,MFC,異構型資料庫,記錄集

一. 問題的提出
  隨著資料庫技術在各個應用領域的迅速發展,市場上推出了多種資料庫,為了充分利用資源,實現資訊共享,以便能對異構型資料庫實現透明的訪問(包括資料查詢、和等功能),作者開發了異構型資料庫通訊平臺。在平臺的研製過程中,獲取各種異構型資料庫的結構資訊是進行資料訪問的前提。作者以VC++5.0為開發語言,利用ODBC實現了這一關鍵技術。

二. ODBC技術介紹
  ODBC技術是指開放性資料庫連線技術,該技術使應用無需關心資料來源來自何種DBMS,利用其標準介面實現與資料來源之間的資料交換。傳統的ODBC是利用高階語言(如C語言)ODBC的API來實現。應用程式要求程式管理器和每個驅動程式為ODBC環境、每個連線以及每個語句分配資訊空間,並返回指向各個儲存區的控制程式碼供其呼叫。ODBC介面定義了三種控制程式碼型別:
環境控制程式碼:為全程資訊標識儲存,包括有效連線控制程式碼及當前活動連線控制程式碼。ODBC將環境控制程式碼定義為HENV型別的變數。應用程式使用單一環境控制程式碼,它必須在連線到資料來源前請求該控制程式碼。
  連線控制程式碼:為特定連線的資訊標識了記憶體儲存。ODBC將連線控制程式碼定義為HDBC型別。應用程式必須在連線到資料來源前請求連線控制程式碼。每個連線控制程式碼與環境控制程式碼有關。然而,環境控制程式碼可以有多個與其有關的連線控制程式碼。
語句控制程式碼:為SQL語句資訊標識記憶體儲存。ODBC將語句控制程式碼定義為HSTMT型別變數。應用程式必須在提交SQL請求之前請求語句控制程式碼。每個語句控制程式碼與一個連線控制程式碼有關。然而,每個連線控制程式碼可以有多個與其相關的語句控制程式碼。
下面以C語言為例說明傳統ODBC程式設計的一般過程。
1、 環境申請,分配環境控制程式碼
HENV henv;
SQLAllocEnv(&henv);
說明:分配一個環境控制程式碼,支援一個或多個資料來源連線。
2、 連線申請,分配連線控制程式碼
HDBC hdbc;
SQLAllocConnect(henv,&hdbc);
說明:一個連線控制程式碼對應一個資料來源,可以有多個連線控制程式碼。
3、 連線資料來源,用連線控制程式碼連線到資料來源
SQLConnect(hdbc,...);
說明:以對話方塊方式獲取註冊資訊,並連線資料來源。
4、 語句申請,分配語句控制程式碼
SQLAllocStmt(hdbc,&hstmt);
說明:獲得語句控制程式碼,以便SQL語句。
5、 執行SQL語句
SQLExecDirect(hstmt,SQLStatement,..);
說明:利用語句控制程式碼,執行SQL語句。
6、 釋放所有資源
SQLfreeStemt(hstmst,...); //釋放語句控制程式碼
SQLDinnect(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  DoField(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;
  U  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類與文件類、視類結合起來,就可以在視窗裡以一定的方式顯示結構資訊。作者利用以上技術在異構型資料庫通訊平臺上成功實現了對各種異構型資料庫庫結構資訊的獲取。

參考文獻:
Mircosoft 《ODBC 2.0 Programmer's Reference and SDK Gu》
利用VC++獲取異構型資料庫庫結構資訊


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988224/,如需轉載,請註明出處,否則將追究法律責任。

相關文章