資料庫編程總結
當前各種主流資料庫有很多,包括Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, Interbase / Firebird, PostgreSQL, SQLite, SAP/DB, TimesTen, MS ACCESS等等。資料庫編程是對資料庫的建立、讀寫等一列的操作。資料庫編程分為資料庫用戶端編程與資料庫伺服器端編程。資料庫用戶端編程主要使用ODBC API、ADO、ADO.NET、OCI、OTL等方法;資料庫服務端編程主要使用OLE DB等方法。資料庫編程需要掌握一些訪問資料庫技術方法,還需要注意怎麼設計高效的資料庫、資料庫管理與啟動並執行最佳化、資料庫語句的最佳化。
一、訪問資料庫技術方法
資料庫編程分為資料庫用戶端編程與資料庫伺服器端編程。資料庫用戶端編程主要使用ODBC API、ADO、ADO.NET、OCI、OTL等方法;資料庫服務端編程主要使用OLE DB等方法。
1、幾種是資料庫存取方法比較
ODBC API是一種適合資料庫底層開發的編程方法,ODBC API提供大量對資料來源的操作,ODBC API能夠靈活地操作遊標,支援各種幫定選項,在所有ODBC相關編程中,API編程具有最高的執行速度。
DAO提供了很好的資料庫編程的物件模型.但是,對資料庫的所有調用以及輸出的資料都必須通過Access/Jet資料庫引擎,這對於使用資料庫應用程式,是嚴重的瓶頸。
OLE DB提供了COM介面,與傳統的資料庫介面相比,有更好的健壯性和靈活性,具有很強的錯誤處理能力,能夠同非關係資料來源進行通訊。
ADO最主要的優點在於便於使用、速度快、記憶體支出少和磁碟遺迹小。
ADO.NET 是利用資料集的概念將資料庫資料讀入記憶體中,然後在記憶體中對資料進行操作,最後將資料集資料回寫到來源資料庫中。
OTL 是 Oracle, Odbc and DB2-CLI Template Library 的縮寫,是一個C++編譯中操控關聯式資料庫的模板庫, OTL中直接操作Oracle主要是通過Oracle提供的OCI介面進行,進行操作DB2資料庫則是通過CLI介面來進行,至於MS的資料庫和其它一些資料庫,則OTL只提供了ODBC來操作的方式。當然Oracle和DB2也可以由OTL間接使用ODBC的方式來進行操縱。具有以下優點:跨平台;運行效率高,與C語言直接調用API相當;開發效率高,起碼比ADO.net使用起來更簡單,更簡潔;部署容易,不需要ADO組件,不需要.net framework 等。
2、VC資料庫編程幾種方法
VC資料庫編程幾種方法,包括ODBC串連、MFC ODBC串連、DAO串連、OLE DB、OLE DB Templates串連、ADO、Oracle專用方法(OCI(Oracle Call Interface)訪問、Oracle Object OLE C++ Class Library )。
<1.>通用方法
1. ODBC串連
ODBC(Open DataBase Connectivity)是MSOA的一部分,是一個標準資料庫介面。它提供對關聯式資料庫訪問的統一介面,實現對異構資料來源的一致訪問。
ODBC資料訪問由以下部分組成:
<1>控制代碼(Handles):ODBC使用控制代碼來標識ODBC環境、串連、語句和描述器.
<2>緩衝區(Buffers):
<3>資料類型(Data types)
<4>一致性層級(Conformance levels)
用ODBC設計用戶端的一般步驟:
<1>分配ODBC環境
<2>分配串連控制代碼
<3>串連資料來源
<4>構造和執行SQL語句
<5>獲得查詢結果
<6>斷開資料來源的串連
<7>釋放ODBC環境
ODBC API是一種適合資料庫底層開發的編程方法,ODBC API提供大量對資料來源的操作,ODBC API能夠靈活地操作遊標,支援各種幫定選項,在所有ODBC相關編程中,API編程具有最高的執行速度.因此,ODBC API編程屬於底層編程。
2. MFC ODBC串連
MFC ODBC是MFC對ODBC進行的封裝,以簡化對ODBC API的 調用,從而實現物件導向的資料庫編程介面.
MFC ODBC的封裝主要開發了CDatabase類和CRecordSet類
(1) CDatabase類
CDatabase類用於應用程式建立同資料來源的串連。CDatabase類中包含一個m_hdbc變數,它代表了資料來源的串連控制代碼。如果要建立CDatabase類的執行個體,應先調用該類的建構函式,再調用Open函數,通過調用,初始化環境變數,並執行與資料來源的串連。在通過Close函數關閉資料來源。
CDatabase類提供了對資料庫進行操作的函數及事務操作。
(2) CRecordSet類
CRecordSet類定義了從資料庫接收或者發送資料到資料庫的成員變數,以實現對資料集的資料操作。
CRecordSet類的成員變數m_hstmt代表了定義該記錄集的SQL語句控制代碼,m_nFields為記錄集中欄位的個數,m_nParams為記錄集所使用的參數個數。
CRecordSet的記錄集通過CDatabase執行個體的指標實現同資料來源的串連,即CRecordSet的成員變數m_pDatabase.
MFC ODBC編程更適合於介面型資料庫應用程式的開發,但由於CDatabase類和CRecordSet類提供的資料庫操作函數有限,支援的遊標類型也有限,限制了高效的資料庫開發。在編程層次上屬於進階編程。
應用執行個體:
1.開啟資料庫
CDatabase database;
database.OpenEx( _T( "DSN=zhuxue" ),CDatabase::noOdbcDialog);//zhuxue為資料來源名稱
2.關聯記錄集
CRecordset recset(&database);
3.查詢記錄
CString sSql1="";
sSql1 = "SELECT * FROM tablename" ;
recset.Open(CRecordset::forwardOnly, sSql1, CRecordset::readOnly);
int ti=0;
CDBVariant var;//var可以轉換為其他類型的值
while (!recset.IsEOF())
{
//讀取Excel內部數值
recset.GetFieldValue("id",var);
jiangxiang[ti].id=var.m_iVal;
recset.GetFieldValue("name", jiangxiang[ti].name);
ti++;
recset.MoveNext();
}
recset.Close();//關閉記錄集
4.執行sql語句
CString sSql="";
sSql+="delete * from 院系審核";//清空表
database.ExecuteSQL(sSql);
sSql也可以為Insert ,Update等語句
5.讀取欄位名
sSql = "SELECT * FROM Sheet1" ; //讀取的檔案有Sheet1表的定義,或為本程式產生的表.
// 執行查詢語句
recset.Open(CRecordset::forwardOnly, sSql, CRecordset::readOnly);
int excelColCount=recset.GetODBCFieldCount();//列數
CString excelfield[30];
//得到記錄集的欄位集合中的欄位的總個數
for( i=0;i<excelColCount;i++)
{
CODBCFieldInfo fieldinfo;
recset.GetODBCFieldInfo(i,fieldinfo);
excelfield[i].name =fieldinfo.m_strName;//欄位名
}
6.開啟excel檔案
CString sDriver = "MICROSOFT EXCEL DRIVER (*.XLS)"; // Excel安裝驅動
CString sSql,sExcelFile; //sExcelFile為excel的檔案路徑
TRY
{
// 建立進行存取的字串
sSql.Format("DRIVER={%s};DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=/"%s/";DBQ=%s",sDriver, sExcelFile, sExcelFile);
// 建立資料庫 (既Excel表格檔案)
if( database.OpenEx(sSql,CDatabase::noOdbcDialog) )
{
//可以把excel作為一個資料庫操作
}
}
catch(e)
{
TRACE1("Excel驅動沒有安裝: %s",sDriver);
AfxMessageBox("讀取失敗,請檢查是否定義資料區Sheet1");
}
3. DAO串連
DAO(Data Access Object)是一組Microsoft Access/Jet資料庫引擎的COM自動化介面.DAO直接與Access/Jet資料庫通訊.通過Jet資料庫引擎,DAO也可以同其他資料庫進行通訊。DAO還封裝了Access資料庫的結構單元,通過DAO可以直接修改Access資料庫的結構,而不必使用SQL的資料定義語言 (Data Definition Language)(DDL)。
DAO的體繫結構如下:
DAO封裝的類:
(1)CdaoWorkspace:對DAO工作區(資料庫處理交易管理員)的封裝
(2)CdaoDatabase:對DAO資料庫物件的封裝,負責資料庫連接.
(3)CdaoRecordset:對DAO記錄集對象的封裝,代表所選的一組記錄.
(4)CdaoTableDef:對錶定義對象的封裝,代表基本表或附加表定義.
(5)CdaoQueryDef:對查詢對象的封裝,包含所有查詢的定義.
(6)CdaoException:DAO用於接收資料庫操作異常的類.
(7)CDaoFieldExchange
DAO提供了很好的資料庫編程的物件模型.但是,對資料庫的所有調用以及輸出的資料都必須通過Access/Jet資料庫引擎,這對於使用資料庫應用程式,是嚴重的瓶頸。
DAO相對於ODBC來說,屬於高層的資料庫介面.
4. OLE DB串連
OLE DB對ODBC進行了兩方面的擴充:一是提供了資料庫編程的OLE介面即COM,二是提供了一個可用於關係型和非關係型資料來源的介面。
OLE DB提供了COM介面,與傳統的資料庫介面相比,有更好的健壯性和靈活性,具有很強的錯誤處理能力,能夠同非關係資料來源進行通訊。
與ODBC API一樣,OLE DB也屬於底層的資料庫編程介面,OLE DB結合了ODBC對關聯式資料庫的操作功能,並進行擴充,可以訪問非關聯式資料庫。
OLE DB訪問資料庫的原理如下:
OLE DB程式結構:
OLE DB由客戶(Consumer)和伺服器(Provider)。客戶是使用資料的應用程式,它通過OLE DB介面對資料提供者的資料進行訪問和控制。OLE DB伺服器是提供OLE DB介面的軟體組件。根據提供的內容可以分為資料提供者(Data Provider)和服務提供者(Service Provider)。
程式結構原理圖如下:
<1>資料提供者
資料提供者擁有自己的資料並把資料以表格的形式呈現給使用者使用.
<2>服務提供者
服務提供者是資料提供者和使用者的結合。它是OLE DB體繫結構中的中介軟體,它是OLE DB資料來源的使用者和資料使用程式的提供者
<3>資料使用程式
資料使用程式對儲存在資料提供者中的資料進行使用和控制.
OLE DB開發程式的一般步驟:
<1>初始化COM環境
<2>串連資料來源
<3>開啟對話
<4>執行命令
<5>處理結果
<6>清除對象
應用執行個體:
使用OLEDB編寫資料庫應用程式
1 概述
OLE DB的存在為使用者提供了一種統一的方法來訪問所有不同種類的資料來源。OLE DB可以在不同的資料來源中進行轉換。利用OLE DB,用戶端的開發人員在進行資料訪問時只需把精力集中在很少的一些細節上,而不必弄懂大量不同資料庫的訪問協議。
OLE DB是一套通過COM介面訪問資料的ActiveX介面。這個OLE DB介面相當通用,足以提供一種訪問資料的統一手段,而不管儲存資料所使用的方法如何。同時,OLE DB還允許開發人員繼續利用基礎資料庫技術的優點,而不必為了利用這些優點而把資料移出來。
2 使用ATL使用OLE DB資料使用程式
由於直接使用OLE DB的對象和介面設計資料庫應用程式需要書寫大量的代碼。為了簡化程式設計,Visual C++提供了ATL模板用於設計OLE DB資料應用程式和資料提供者。
利用ATL模板可以很容易地將OLE DB與MFC結合起來,使資料庫的參數查詢等複雜的編程得到簡化。MFC提供的資料庫類使OLE DB的編程更具有物件導向的特性。Viual C++所提供用於OLE DB的ATL模板可分為資料提供者的模板和資料使用程式的模板。
使用ATL模板建立資料應用程式一般有以下幾步驟:
1)、 建立應用程式框架
2)、 加入ATL產生的模板類
3)、 在應用中使用產生的Data Access Objects
3 不用ATL使用OLE DB資料使用程式
利用ATL模板產生資料使用程式較為簡單,但適用性不廣,不能動態適應資料庫的變化。下面我們介紹直接使用MFC OLE DB類來產生資料使用程式。
模板的使用
OLE DB資料使用者模板是由一些模板組成的,包括如下一些模板,下面對一些常用類作一些介紹。
1)、 會話類
CDataSource類
CDataSource類與OLE DB的資料來源對象相對應。這個類代表了OLE DB資料提供者和資料來源之間的串連。只有當資料來源的串連被建立之後,才能產生會話對象,可以調用Open來開啟資料來源的串連。
CSession類
CSession所建立的對象代表了一個單獨的資料庫訪問的會話。一個用CDataSource類產生的資料來源對象可以建立一個或者多個會話,要在資料來源對象上產生一個會話對象,需要調用函數Open()來開啟。同時,會話對象還可用於建立事務操作。
CEnumeratorAccessor類
CEnumeratorAccessor類是用來訪問列舉程式查詢後所產生的行集中可用資料提供者的資訊的訪問器,可提供當前可用的資料提供者和可見的訪問器。
2)、 訪問器類
CAcessor類
CAccessor類代表與訪問器的類型。當使用者知道資料庫的類型和結構時,可以使用此類。它支援對一個行集採用多個訪問器,並且,存放資料的緩衝區是由使用者指派的。
CDynamicAccessor類
CDynamicAccessor類用來在程式運行時動態建立訪問器。當系統運行時,可以動態地從行集中獲得列的資訊,可根據此資訊動態地建立訪問器。
CManualAccessor類
CManualAccessor類中以在程式運行時將列與變數綁定或者是將參數與變數捆定。
3)、 行集類
CRowSet類
CRowSet類封裝了行集對象和相應的介面,並且提供了一些方法用於查詢、設定資料等。可以用Move()等函數進行記錄移動,用GetData()函數讀取資料,用Insert()、Delete()、SetData()來更新資料。
CBulkRowset類
CBulkRowset類用於在一次調用中取回多個行控制代碼或者對多個行進行操作。
CArrayRowset類
CArrayRowset類提供用數組下標進行資料訪問。
4)、 命令類
CTable類
CTable類用於對資料庫的簡單訪問,用資料來源的名稱得到行集,從而得到資料。
CCommand類
CCommand類用於支援命令的資料來源。可以用Open()函數來執行SQL命令,也可以Prepare()函數先對命令進行準備,對於支援命令的資料來源,可以提高程式的靈活性和健壯性。
在stdafx.h標頭檔裡,加入如下代碼。
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include <atldbcli.h>
#include <atldbsch.h> // if you are using schema templates
在stdafx.cpp檔案裡,加入如下代碼。
#include <atlimpl.cpp>
CComModule _Module;
決定使用何種類型的存取程式和行集。
擷取資料
在開啟資料來源,會話,行集對象後就可以擷取資料了。所擷取的資料類型取決於所用的存取程式,可能需要繫結資料行。按以下步驟。
1、 用正確的命令開啟行集對象。
2、 如果使用CManualAccessor,在使用之前與相應列進行綁定。要繫結資料行,可以用函數GetColumnInfo,如下所示:
// Get the column information
ULONG ulColumns = 0;
DBCOLUMNINFO* pColumnInfo = NULL;
LPOLESTR pStrings = NULL;
if (rs.GetColumnInfo(&ulColumns, &pColumnInfo, &pStrings) != S_OK)
AfxThrowOLEDBException(rs.m_pRowset, IID_IColumnsInfo);
struct MYBIND* pBind = new MYBIND[ulColumns];
rs.CreateAccessor(ulColumns, &pBind[0], sizeof(MYBIND)*ulColumns);
for (ULONG l=0; l<ulColumns; l++)
rs.AddBindEntry(l+1, DBTYPE_STR, sizeof(TCHAR)*40, &pBind[l].szValue, NULL, &pBind[l].dwStatus);
rs.Bind();
3、 用while迴圈來取資料。在迴圈中,調用MoveNext來測試游標的傳回值是否為S_OK,如下所示:
while (rs.MoveNext() == S_OK)
{
// Add code to fetch data here
// If you are not using an auto accessor, call rs.GetData()
}
4、 在while迴圈內,可以通過不同的存取程式擷取資料。
1) 如果使用的是CAccessor類,可以通過使用它們的資料成員進行直接存取。如下所示:
2) 如果使用的是CDynamicAccessor 或CDynamicParameterAccessor 類,可以通過GetValue或GetColumn函數來擷取資料。可以用GetType來擷取所用資料類型。如下所示:
while (rs.MoveNext() == S_OK)
{
// Use the dynamic accessor functions to retrieve your
// data
ULONG ulColumns = rs.GetColumnCount();
for (ULONG i=0; i<ulColumns; i++)
{
rs.GetValue(i);
}
}
3) 如果使用的是CManualAccessor,可以指定自己的資料成員,綁定它們。就可以直接存取。如下所示:
while (rs.MoveNext() == S_OK)
{
// Use the data members you specified in the calls to
// AddBindEntry.
wsprintf("%s", szFoo);
}
決定行集的資料類型
在運行時決定資料類型,要用動態或手工的存取程式。如果用的是手工存取程式,可以用GetColumnInfo函數得到行集的列資訊。從這裡可以得到資料類型。
4 總結
由於現在有多種資料來源,,想要對這些資料進行訪問管理的唯一途徑就是通過一些同類機制來實現,如OLE DB。進階OLE DB結構分成兩部分:客戶和提供者。客戶使用由提供者產生的資料。
就像其它基於COM的多數結構一樣,OLE DB的開發人員需要實現很多的介面,其中大部分是模板檔案。
當產生一個客戶對象時,可以通過ATL對象嚮導指向一個資料來源而建立一個簡單的客戶。ATL對象嚮導將會檢查資料來源並建立資料庫的用戶端代理。從那裡,可以通過OLE DB客戶模板使用標準的瀏覽函數。
當產生一個提供者時,嚮導提供了一個很好的開端,它們僅僅是產生了一個簡單的提供者來列舉某一目錄下的檔案。然後,提供者模板包含了OLE DB支援的完全補充內容。在這種支援下,使用者可以建立OLE DB提供者,來實現行集定位策略、資料的讀寫以及建立書籤。
應用案例:
Visual C++中使用OLE DB讀寫SQL Server
在需要對資料庫進行操作時,OLE DB總是被認為是一種效率最高但最難的方法。但是以我最近使用OLE DB的經驗看來,OLE DB的效率高則高矣,但卻一點都不難。說它難恐怕主要是因為可參考的中文資料太少,為了協助以後需要接觸OLE DB的同行,我撰寫了這篇文章。本文包含如下內容:
1. OLE DB寫資料庫;
2. OLE DB讀資料庫;
3. OLE DB對位元據(text、ntext、image等)的處理。
首先來看看對SQL Server進行寫操作的代碼,有一定VC基礎的讀者應該可以很順利地看懂。OLE DB寫資料庫,就是這麼簡單。
注:
1.以下代碼中使用的模板類EAutoReleasePtr<T>與ATL中的CComPtr<T>