1. 程式人生 > >MFC ODBC 詳細用法

MFC ODBC 詳細用法

主要內容:

  1. MFC ODBC將ODBC API封裝在類CDatabase、CRecordSet、CFieldExchange、CRecordView和CDBException中
  2. 使用MFC ODBC開發資料庫應用程式的一般步驟
  3. 使用AppWizard訪問資料庫
  4. 使用類CDatabase連線資料庫
  5. 使用類CRecordSet開啟記錄集、獲取資料
  6. 使用類CRecordSet的函式MoveFirst()、MoveLast()、MoveNext()、MovePrev()、IsBOF()和IsEOF()進行記錄集的遍歷
  7. 使用類CRecordSet的函式AddNew和Update增加記錄
  8. 使用類CRecordSet的函式Edit和Update修改記錄
  9. 使用類CRecordSet的函式Delete刪除記錄
  10. 使用類CDatabase的函式ExecuteSQL直接執行SQL命令
  11. 使用類CDatabase的函式BeginTrans、CommitTrans和Rollback處理事務

MFC OBDC技術

概述

MFC的ODBC類對較複雜的ODBC API進行了封裝,提供了簡化的呼叫介面。MFC的ODBC類主要包括以下5個類:

  1. CDatabase類:主要功能是建立與資料來源的連線
  2. CRecordset類:代表從資料來源選擇的一組記錄(記錄集)
  3. CRecordView類:提供了一個表單檢視與某個記錄集直接相連,利用對話方塊資料交替機制(DDX)在記錄集與表單檢視的控制元件之間傳輸資料
  4. CFieldExchange類:支援記錄欄位資料交換(RFX),即記錄集欄位資料成員與相應的資料庫的表的欄位之間的資料交換。
  5. CDBException類:代表ODBC類產生的異常。

CDatabase類操作資料來源

CDatabase型別的物件表示一個到資料來源的連線,通過它可以操作資料來源。

該類的成員函式如下表:

函式

說明

CDatabase

構造一個物件

Close

關閉資料來源連線

Open

通過一個ODBC驅動程式建立到資料來源的連線

OpenEx

通過一個ODBC驅動程式建立到資料來源的連線

BeginTrans

開始事務

BindParameters

允許在呼叫CDatabase::ExecuteSQL前繫結引數

Cancel

取消非同步操作或第二條執行緒中的過程

CommitTrans

執行事務

ExecuteSQL

執行SQL語句,不返回記錄

Rollback

回滾事務,資料來源返回先前的狀態

該類的屬性屬性如下表:

屬性

說明

CanTransact

如果資料來源支援事務,返回非零

CanUpdate

如果CDatabase可以更新,返回非零

GetBookmarkPersistence

獲得書籤對記錄集物件的永續性

GetConnect

返回ODBC連線串

GetCursorCommitBehavior

獲得提交事務對記錄集物件的影響

GetCursorRollbackBehavior

獲得回滾事務對記錄集物件的影響

GetDatabaseName

返回當前使用的資料庫名

IsOpen

如果當前CDatabase物件連線到資料來源,返回非零

SetLoginTimeout

設定資料來源連線的超時數(秒為單位)

SetQueryTimeout

設定查詢操作的超時數(秒為單位)

應用程式可使用多個CDatabase型別的物件。構造一個物件並呼叫Open()成員函式開啟一個連線。接著構造CRecordset型別的物件以操作連線的資料來源,構造時向記錄集物件傳遞CDatabase型別的指標。完成使用後,用Close()成員函式銷燬CDatabase型別的物件。

一般情況下並不需要直接使用CDatabase型別的物件,因為CRecordset型別的物件可以實現大多數的功能、但是在進行事務處理時,CDatabase就起到關鍵作用。事務(Transaction)指的是將一系列對資料來源的更新放在一起,同時提交或一個都不提交,為的是確保多使用者對資料來源同時操作時的資料正確性。

CRecordset類操作記錄集

一個CRecordset型別的物件代表從資料來源選擇的一組記錄的集合——記錄集,通過該類的方法實現對資料庫中記錄的各種操作。

該類常用的資料成員如下表:

成員

說明

m_hstmt

包含記錄集的ODBC陳述控制代碼,型別為HSTMT

m_nFields

包含記錄集中欄位資料成員的數量,型別為UNIT

m_nParams

包含記錄集中引數資料成員的數量,型別為UNIT

m_pDatabase

包含一個CDatabase物件指標,通過它訪問資料來源

m_strFilter

包含CString物件,定義SQL中WHERE子句

m_strSort

包含CString物件,定義SQL中ORDER BY子句

該類的構造方法如下表:

構造方法

說明

Close

關閉記錄集和與之相關的HSTMT

CRecordset

構造一個CRecordset物件

Open

通過獲得表或執行記錄集所代表的查詢來開啟記錄集

CRecordset類記錄集屬性如下表:

屬性

說明

CanAppend

如果新記錄可以通過Addnew新增到記錄集,返回非零

CanBookmark

如果記錄集支援書籤,返回非零

CanRestart

如果Requery可以被呼叫來再次執行記錄集查詢,返回非零

CanScroll

如果可以在記錄中回滾,返回非零

CanTransact

如果資料來源支援事務,返回非零

CanUpdate

如果記錄集可以被更新,返回非零

GetODBCFieldCount

返回記錄集中欄位的數量

GetRecordCount

返回記錄集中記錄的數量

GetSQL

獲得SQL字串

GetStatus

獲得記錄集的狀態

GetTableName

獲得記錄集所屬的表名

IsBOF

如果記錄集定位在第一條記錄之前,返回非零

IsDeleted

如果記錄集定位在一條刪除的記錄,返回非零

IsEOF

如果記錄集定位在最後一條記錄之後,返回非零

IsOpen

如果呼叫過Open函式,返回非零

CRecordset類更行操作如下表:

更新操作

說明

AddNew

準備增加一條新紀錄,呼叫Update之後完成新增

CancelUpdate

取消任何未完成的更新

Delete

從記錄集中刪除當前記錄

Edit

準備對當前記錄進行修改,呼叫Update後完成修改

Update

通過將新記錄或編輯的資料存入資料來源來完成AddNew或Edit操作

記錄集有兩種形式:snapshot(表示資料的靜態檢視)和dynaset(表示記錄集與其他使用者對資料庫的更新保持同步)。

CFieldExchange類處理資料交換

CFieldExchange類支援資料庫類所使用的記錄集欄位交換(RFX)程式。如果使用自定義的資料型別寫資料交換程式,會使用這個類。否則不會直接使用此類。RFX在記錄集物件的欄位資料成員與資料來源中當前記錄的相應欄位之間交換資料。

CRecordView類顯示記錄

CRecordView物件用於在控制元件中顯示資料庫記錄的檢視。這種檢視是一種直接連到一個CRecordView物件的格式檢視,它從一個對話方塊模板建立資源,並將CRecordView物件的欄位顯示在對話方塊模版的控制元件裡。物件利用DDX和RFX機制,使窗體上的空間和記錄集的欄位值之間資料移動自動化,也就是說,使用者不需要編寫一行程式碼就可以完成簡單的資料庫記錄檢視程式。

CDBException類處理異常

由CException類派生,以3個繼承的成員變數反映對資料庫操作時的異常:

  1.  m_nRetCode:以ODBC返回程式碼(SQL_RETURN)的形式表明造成一場的原因
  2. m_strError:字串,描述造成丟擲異常的錯誤原因
  3. m_strStateNativeOrigin:字串,用以描述以ODBC錯誤程式碼表示的異常錯誤

使用MFC ODBC程式設計建立應用程式

MFC ODBC程式設計模型概述

  1. 使用MFC ODBC訪問資料庫比直接使用ODBC API簡單得多,程式設計步驟如下:
  2. 使用CDatabase開啟資料來源的連線,如果利用AppWizard生成一個ODBC資料庫應用程式,則會自動完成操作。
  3. 使用ClassWizard嚮導加入由CRecordset類派生的使用者記錄集類,完成對資料庫的繫結。
  4. 建立記錄集類物件,如果利用AppWizard生成一個ODBC資料庫應用程式,則會自動在文件類中建立。
  5. 使用記錄集物件對資料庫進行遍歷、增加、刪除、修改等操作。
  6. 使用CDatabase類的ExecuteSQL函式直接執行SQL命令。
  7. 使用CDatabase類的BeginTrans、CommitTrans和Rollback函式進行事務處理
  8. 使用CDatabase類的Close函式關閉資料來源連線。

通過AppWizard建立資料庫應用程式

“New”-->”MFC AppWizard(EXE)”-->”OK”->”Single document(單文件)”-->NEXT -->”選擇需要對什麼樣的資料庫型別支援做出選擇:None(不要任何資料支援,今後再新增很麻煩)、Header files only(該工程需要資料庫支援,但不清楚細節時選擇。工程會新增所要求的標頭檔案和連結庫,但必須自己在建立資料庫類)、Database view without file support(表示包含資料庫標頭檔案和連結庫,並建立記錄集和記錄檢視,應用程式雖有文件支援,但不支援序列化)、Database view with file support(表示包含資料庫標頭檔案、記錄集和檢視外,程式還支援序列化) -->”Data Options 對話方塊:在”Datasource”中選擇ODBC單選按鈕,選擇一個已經註冊號的資料來源(例項程式Sample中使用的資料來源叫students)。在”Recordset type”(記錄型別)中有三個選項:Snapshot(快照:它是當前表的一個靜態檢視。表開啟之後,表中的所有資料馬上被載入到應用程式中。其他使用者或程式對錶的修改只有在下次開啟表時才會體現出來,看不到其他使用者對錶的”即時”修改,因此它是靜態的。Snapshot適用於使用者查詢資訊(例如生成報表等)而不適用於資料編輯。)、Dynaset(動態集:這個選項建立指向所請球的每個記錄的實際指標。只有螢幕需要顯示記錄時,才從資料庫中提取資料。這種方式的好處是動態、即時的瀏覽到當前的記錄。而 其他使用者也會即時看到你對記錄所做出的修改。該選項適合於建立使用者要發費很多時間來編輯資料的應用程式,並且,如果正在編寫大型資料庫應用程式,他也是最 佳選擇)、Table(表:表方法(僅使用DAO訪問資料庫時可用)把所做查詢的內容放進一個臨時表。這樣做不但減小了從伺服器下載的資訊量,還意味著程式設計師有了更大的靈活性,因為可以直接操作臨時表字段和記錄。但缺點是你看不到別人的修改。使用DAO且使用者會執行同等數量的資料查詢和資料編輯時,它是最佳選擇。)” -->”Selete Database Table(選擇資料庫表)” -->”Finish”.

通過AppWizard建立了一個MFC ODBC資料庫工程後,該工程與一般的應用程式工程有所不同:多了一個CRecordset的派生類,對應前面選擇的資料庫表;檢視類的基類是CRecordView,負責顯示資料;工具欄上多了遍歷記錄集的4個按鈕。

使用CDatabase類方法開啟資料來源

通過CDatabase類的Open函式來開啟資料來源,該函式原型如下:

virtual BOOL Open(

LPCTSTR lpszDSN, //一個數據源名,此資料來源名是通過ODBC管理器註冊的。如果DSN被設定在lpszConnect裡,那麼lpszDSN不應在被重新設定,lpszDSN應設為NULL。如果沒有設定lpszConnect,而且又把lpszDSN設定為NULL,那麼將出現一個對話方塊,讓使用者選擇資料來源。

BOOL bExclusive = FALSE, //預設為FALSE,表示以共享方式開啟資料來源。當前版本的類庫不支援獨佔方式,如果設定為TRUE,將失敗

BOOL bReadOnly = FALSE, //如果希望連線以只讀方式開啟,不想對資料來源進行更新,那麼設定為TRUE,所有依靠此連線開啟的記錄集全部繼承此屬性。預設值為FALSE

LPCTSTR lpszConnect = _T(“ODBC;”), //連線串。連線串可能包含資料來源名、資料來源中使用者的ID、密碼和其他資訊。整個連線串必須以”ODBC;”開頭。”ODBC;”表示連線是一個ODBC資料來源。

BOOL bUseCursorLib = TRUE //如果希望載入ODBC游標動態連線庫,設定為TRUE

);

下面的例子表示如何開啟資料來源:

//在文件類中加入
//CDatabase類物件
CDatabase m_pdatabase;
//連線物件到一個數據源(沒有密碼),ODBC連線對話方塊將是中隱藏
m_pdatabase.Open(_T(“students”), FALSE, FALSE, _T(“ODBC; UID = Admin”));
//或者也可以顯示ODBC對話方塊,請使用者提供連線資訊
m_pdatabase.Open(NULL);

像程式Sample一樣,通過嚮導生成資料庫工程,不用新增程式碼就能實現對資料來源的開啟。在CRecordset類中有一個名為GetDefaultConnect()的虛擬函式值得注意,通過呼叫它可以返回預設的資料來源連線(也就是在生成工程的時候所選擇的資料來源)來開啟資料來源。該函式如下:

CString CSampleSet::GetDefaultConnect()
{
return _T(“ODBC; DSN = students”);
}

使用CRecordste類開啟記錄集

通過宣告CRecordset記錄集類的物件,再利用記錄集類的Open()函式可開啟記錄集,從而獲取資料庫中表的資料。也正是在呼叫Open()函式後,記錄集當中的成員變數得到資料來源中表的欄位值。Open()函式的形式如下:

virtual BOOL Open(
UNIT nOpenType = AFX_DB_USE_DEFAULT_TYPE,
LPCTSTR lpszSQL = NULL,
DWORD dwOptions = none
);

其中nOpenType為開啟的型別,可取只有一下4種:

  1. CRecordset::dynaset:雙向滾動的記錄集,在記錄集開啟時,記錄的順序和成員就被確定了。其他使用者對資料的修改在fetch操作之後才可訪問,這也被叫做鍵集驅動的記錄集。
  2. CRecordset::dynamic:雙向滾動的記錄集,其他使用者對資料的修改在fetch操作後才可訪問。許多ODBC驅動程式都不支援此種記錄集。
  3. CRecordset::ForwardOnly:只讀記錄集,只向前滾動。

第二個引數lpszSQL是一個CString的指標,指向以下內容之一:

  1. 一個NULL指標;
  2. 一個表名;
  3. 一個SQL查詢語句

第三個引數dwOptions為一系列選項的組合,它的預設值為None。可以像下面這樣呼叫Open()函式來開啟記錄集:

CDatabase m_pdatabase;
m_pdatabase.Open(_T(“students”), FALSE, FALSE, _T(“ODBC; UID=Admin”));
CRecordset Sample(&m_pdatabase);
Sample.Open(CRecordset::dynaset, _T(“Select name from students”));

以上語句先用CDatabase物件開啟一個數據源,之後構造記錄及物件,最後記錄及物件Sample以動態方式開啟students表中的name欄位。

下面的語句表示以全部預設方式執行記錄記得開啟操作:

CDatabase m_pdatabase;
m_pdatabase.Open(_T(“students”), FALSE, FALSE, _T(“ODBC; UID=Admin”));
CRecordset Sample(&m_pdatabase);
Sample.Open();

也可以像下面程式碼中那樣開啟一個表中的所有欄位:

CDatabase m_pdatabase;
m_pdatabase.Open(_T(“students”), FALSE, FALSE, _T(“ODBC; UID=Admin”));
CRecordset Sample(&m_pdatabase);
Sample.Open(CRecordset::dynaset, _T(“Select * from students”));

經過上面的操作,記錄集物件的欄位變數就獲得了資料庫中特定表中指定欄位的資料。

像程式Sample一樣,在嚮導當中選擇資料來源和表名。在CRecordset類中有一個名為GetDefaultSQL()的虛擬函式值得注意,通過呼叫它可以返回預設的SQL語句,用於形成記錄集物件。該函式如下:

CString CSampleSet::GetDefaultSQL()
{
return _T(“[students]”);
}

繫結記錄集

通過嚮導建立工程後,程式的框架就生成出來。如果開啟CRecordset的派生類CSampleSet,會發現裡面已經有了5個變數:

//Field/Param Data
//{{AFX_FIELD(CSampleSet, CRecordset)
long m_id;
CString m_name;
Long m_department;
Long m_age;
CString m_comment;
//}}AFX_FIELD

這5個變數正好與要訪問的表中的欄位同名,並且變數的型別也與欄位型別一致。這是MFC自動新增的變數,已繫結表中的欄位。如果表中的欄位是中文的,那麼MFC會建立m_column1、m_column2等與之對應。RFX實現了這種繫結。

RFX(Record Field Exchange),記錄欄位交換使記錄集(CREcordset)和隱藏於後臺的資料來源(Datasource)之間建立其對應的關係。使用者只需要操作記錄集,就可以實現對資料來源的操作。MFC中提供了一組RFX呼叫函式,利用這些函式,就可以使記錄集中的變數與資料來源中的欄位對應起來。使記錄集和資料之間進行資料交換成為可能,並且,這種交換是雙向的。

可以在CSampleSet類的DoFieldExchange函式中看到一組組的RFX函式呼叫,正是通過呼叫它們,使CSampleSet記錄集中的變數與students表中的欄位對應起來。

void CSampleSet:: DoFieldExchange(CFieldExchange *pFx)
{//{{AFX_FIELD_MAP(CSampleSet)
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Long(pFX, _T(“[id]”), m_id);
RFX_Long(pFX, _T(“[name]”), m_name);
RFX_Long(pFX, _T(“[department]”), m_department);
RFX_Long(pFX, _T(“[age]”), m_age);
RFX_Long(pFX, _T(“[comment]”), m_comment);
//}}AFX_FIELD_MAP
}

RFX函式通常有3個引數(個別的會有4或5個)。第一個引數為一個指向CFieldExchange類物件的指標,第二個引數為資料來源中的一個欄位名稱,第三個引數是與欄位相對應的記錄集中的變數名。常用的RFX函式如下表:

函式

資料型別

RFX_Bool

BOOL

RFX_Byte

BYTE

RFX_Binary

CByteArray

RFX_Double

Double

RFX_Sing

Float

RFX_Int

Int

RFX_Long

Long

RFX_LongBinary

CLongBinary

RFX_Text

CString

RFX_Date

CTime

引數化記錄集和查詢

CRecordset類物件中有兩個成員變數,一個為m_strFilter(過濾字串,負責對記錄集進行過濾,返回過濾後的記錄),另一個為m_strSort(排序字串,對記錄集進行排序)。

m_strFilter存放著SQL語句中WHERE子句的條件字串,m_strSort中則存放著SQL語句中ORDER BY子句的字串。經過對它們的賦值,可以更加靈活的獲得資料庫中特定的資料,以及對記錄進行排序。

下面的程式碼中向m_strFilter賦值“comment=good”,向m_strSort賦值“name”:

CRecordset Sample;
Sample.m_strFilter = “comment = good”;
Sample.m_strSort = “name”;

除了直接向m_strFilter賦值外,還可以使用引數化。利用引數化可以更直觀,更方便地完成條件查詢任務。使用引數化的步驟如下:

//首先宣告參變數:
CString age1;
CString comment1;

//在建構函式中初始化參變數
age1 = _T(“”);
comment = _T(“”);

//將參變數與對應列繫結
pFX->SetFieldType(CFieldExchange::param);
pFX->Text(pFX, _T(“[age]”), age1);
pFX->Text(pFX, _T(“[comment]”), comment1);

//最後利用參變數進行條件查詢
m_pSet->m_strFilter = “age= ? AND comment = ?”;
m_pSet->age = “21”;
m_pSet->comment = “good”;
m_pSet->Requery();

參變數的值按繫結的順序替換查詢字串中的“?”適配符。程式碼中的m_pSet是CRecordView類的一個記錄集指標,指向當前文件類中的記錄集變數。它是在CRecordView類的OnInitialUpdate中被賦予文件類下記錄集物件的指標的。下面是程式中CSampleView類的OnInitialUpdate函式體:

void CSampleView::OnInitialUpdate()
{ //為m_pSet賦予文件類下的記錄集物件的指標
m_pSet = &GetDocument()->m_sampleSet;
CRecordView::OnInitialUpdate();
ResizeParentToFit();
}

遍歷記錄集合

CRecordset類中有一組函式負責記錄集指標的移動,例如使用記錄集指標下移一個記錄、使用記錄集指標上移一個記錄等。

  1. MoveFirst()函式:使指標移動到第一條記錄
  2. MoveLast()函式:使指標移動到最後一條記錄
  3. MoveNext()函式:使指標移動到下一條記錄
  4. MovePrev()函式:使指標移動到前一條記錄
  5. IsBOF()函式:當指標移動到第一條記錄前面或者表中沒有記錄的時候返回真
  6. IsEOF()函式:當指標移動到最後一條記錄後面的時候返回真

知道上面的這些函式的意義後,下面來看如何遍歷記錄集。下面的程式碼中m_pSet是一個記錄集指標,m_list為一個列表框控制元件(ClistBox類)的變數:

if(!m_pSet->IsOpen()) //用IsOpen函式檢測記錄集是否開啟
m_pSet->Open();
m_pSet->MoveFirst();
while(!m_pSet->IsEOF())
{
m_list.AddString(m_pSet->m_name);
m_pSet->MoveNext();
}
m_pSet->MoveFirst(); //遍歷完成後,使記錄集指標指向第一條記錄

書籤定位和絕對定位

當在記錄集中瀏覽的時候,可能想返回記錄集中特定的一條記錄,CRecordset提供了兩種方法可以指定記錄集到特定的位置。

1、 書籤定位

可以在記錄集中的某一條記錄增加一個書籤。在記錄集瀏覽時由於使用者的增刪操作使記錄的絕對位置發生改變,所以以來絕對位置是不可靠的。因此需要使用書籤定位來為所想要的記錄定位。CRecordset類中提供的書籤定位的方法是GetBookmark和SetBookmark兩個函式,他們的原型如下:

void GetBookmark(CDBVariant& varBookmark); //引數為CDBVariant的物件
void SetBookmark(const CDBVariant& varBookmark);
//這裡只需直接使用CDBVariant的物件即可。
//建立CDBVariant物件
CDBVariant bookmark;
//rs是CRecordset類或CRecordset類派生類的物件
rs.GetBookmark(bookmark);
//一系列移動到其他記錄的程式碼
rs.MoveNext();
rs.MoveNext();
rs.SetBookmark(bookmark);

GetBookmark函式將當前的記錄存入一個CDBVariant的物件中,經過一系列的紀錄移動之後,在呼叫SetBookmark,並且用剛才記錄“書籤”的CDBVariant物件bookmark作引數來使用當前記錄集重新指向“書籤”的位置。

是否支援書籤定位取決於ODBC驅動程式和記錄集型別。可以通過呼叫CRecordset::CanBookmark來確定是否支援書籤定位。如果想支援書籤定位,還需要在記錄集的Open函式的dwOptions引數位置中加入CRecordset::useBookmarks引數。注意forward-only recordsets(只向前)型別的記錄集也不支援書籤定位。還有一點,就是在某些記錄集操作之後,也應該及時檢查前面所設定的“書籤”是否還可以繼續使用。例如,對一個記錄及進行了Requery操作之後,書籤就可能不再有效了。所以,在呼叫SetBookmark函式之前,應該先呼叫CDatabase::GetBookmarkPersistence函式來核對是否可以安全的呼叫SetBookmark函式。下面是該函式原型:DWORD GetBookmarkPersistence() const;

這個函式的返回值為bitmask,這是一個DWORD型別的返回值。該值可以是下表中的多個bitmask值的組合。

bitmask值

書籤的有效性

SQL_BP_CLOSE

Requery操作後,書籤有效

SQL_BP_DELETE

對某行執行delete操作後,書籤對此行依然有效

SQL_BP_DROP

一次Close操作後,書籤有效

SQL_BP_SCROLL

任何Move操作之後,書籤都有效

SQL_BP_TRANSACTION

一次事務被提交或回滾後,書籤有效

SQL_BP_UPDATE

對某行執行Update操作後,書籤對此行有效

SQL_BP_OTHER_HSTMT

與某記錄集物件相關的書籤對另一記錄集也有效

 2、 絕對定位

相對於書籤定位,絕對定位就好像記住某本書的某一個固定頁碼一樣。絕對定位就是通過原始的記錄位置來設定當前記錄,比如可以設定記錄集中第8條記錄為當前記錄。如想使用絕對定位來改變當前的記錄集位置,可以呼叫CRecordset::SetAbsolutePosition函式。其原型為:

void SetAbsolutePosition(long nRows);

nRows表示記錄集中的一個絕對位置。呼叫該函式會把記錄集指標定位到nRows引數所指行號的記錄上。

下面的程式碼表示把記錄集定位到第12條記錄的位置上:

long row;
row = 12;
rs.SetAbsolutePositon(row);

對於ODBC的記錄集來說,絕對位置1指的是記錄集當中的第一條記錄,絕對位置如果是0則代表的是BOF位置(在第一條記錄之前)。

***forward-only recordsets(只向前)型別的記錄集不支援SetAbsolutePosition方法。此外,記錄的絕對位置存在潛在的不可靠性。如果使用者刪除了某一條記錄,那麼後續記錄的位置都發上變化,並且記錄集也可能被再次重新建立,不能確保某條記錄在建立的記錄集中有與原來相同位置,因此建議使用書籤定位。 ***

獲取記錄集的資料

通過在對話方塊上新增控制元件,並且為控制元件繫結變數來達到資料交換並顯示的目的,與控制元件繫結的變數正是記錄集中的那些欄位變數。步驟如下:

  • 按照所需顯示的欄位數量,在對話方塊窗體上新增幾個編輯框控制元件,將控制元件的ID改成與表中欄位相似的名稱
  • 再按住Ctrl的同時雙擊每一個控制元件,為它們增加成員變數
  • 在Add Member Variable對話方塊中單擊“Member variable name”下拉框,為每個控制元件一次選擇記錄集中的各個變數。

到底記錄集中的變數是如何把資料顯示在控制元件上呢?下面講述DDX技術。

DDX是Dialog Data Exchange的縮寫,即對話方塊資料交換。它在對話方塊的控制元件與記錄集的變數之間建立起一座橋樑,可以使他們雙向交換資料。

CRecordView類裡面有一個DoDataExchange函式,所有的DDX函式都是在DoDataExchange中呼叫的。DoDataExchange函式為DDX函式提供了一個CDataExchange類物件的指標。在工程剛剛建立時,CSampleView::DodataExchange函式是一個空函式,因此此時對話方塊上並沒有控制元件,更沒有與控制元件對應的變數。但經過窗體新增控制元件、為控制元件新增變數之後,再開啟DoDataExchange函式,將會出現下面這樣的程式碼:

void CSampleView::DoDataExchange(CDataExchange* pDX)
{
CRecordView::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSampleView)
DDX_FieldText(pDX, IDC_ID, m_pSet->m_id, m_pSet);
DDX_FieldText(pDX, IDC_NAME, m_pSet->m_name, m_pSet);
DDX_FieldText(pDX, IDC_DEPARTMENT, m_pSet->m_department, m_pSet);
DDX_FieldText(pDX, IDC_AGE, m_pSet->m_age, m_pSet);
DDX_FieldText(pDX, IDC_COMMENT, m_pSet->m_comment, m_pSet);
//}}AFX_DATA_MAP
}

DDX_FieldText函式負責在對話方塊控制元件和記錄集中欄位變數之間建立聯絡。因為記錄集中的欄位變數對應的是資料庫中表的每個欄位,所以就能在控制元件上看到表中的資料了。DDX函式有四個引數,分別為:

  • 一個指向CDateExchange類物件的指標
  • 對話方塊上控制元件的ID值
  • 記錄集中要與物件繫結的欄位變數
  • 記錄集物件的指標

DDX可以管理以下型別資料變數與控制元件的資料交換。它們是short、long、int、DWORD、CString、float、double、BOOL以及BYTE。

程式通過UpdateData這個函式在控制元件與變數之間達到雙向資料,而不是直接呼叫DoDataExchange。UpdateData函式的原型如下:

BOOL UpdateData(BOOL bSaveAndValidate = TRUE);

bSaveAndValidate指示了資料傳輸的方向,當為TRUE時就是控制元件向變數傳輸。反之,就是變數向控制元件傳輸資料。預設值為TRUE

新增記錄

對記錄的操作大多數都是由CRecordset類來負責的,執行新增的任務也不例外。CRecordset類中的函式AddNew()表示向表中新增一條新的紀錄,該函式原型如下:

virtual void AddNew();

執行AddNew()函式之後會新增加一條空記錄,等待輸入資料。此時記錄的每一個欄位都被初始化NULL。在輸入新的記錄資料之後,需呼叫另一個CRecordset類的函式才能完成對新記錄的新增,這個函式是Update(),函式原型為:virtual BOOL Update();

在呼叫完AddNew函式、輸入新資料之後一定要呼叫Update,它負責把新新增的資料儲存到資料來源。實際上AddNew函式只是在記憶體中建立了一塊緩衝區,等待輸入資料,之後需要使用Update來真正把資料存入資料來源。如果在呼叫Update之前滾動到了另一條記錄,那麼新記錄就會丟失,也不會提出警告。對於dynaset(動態集)型別的記錄集,新記錄會新增到末尾。新記錄不能被新增到Snapshot型別的記錄集中。最後還需要呼叫CRecordset::Requery函式以重新整理記錄集:virtual BOOL Requery();

Requery函式負責重新整理記錄集來反映當前最新的資料。在每次對記錄進行新增、刪除後,都有必要呼叫Requery來更新記錄集。呼叫Requery函式後,記錄集指標重新指向第一條記錄。

下面的程式碼實現了在一個名為OnAdd的函式中新增記錄:

void CSampleView::OnAdd()
{
//新增一條新記錄
m_pSet->AddNew();
//對記錄集中的m_id、m_name、m_department、m_age等賦值
m_pSet->m_id = “1009”;
m_pSet->m_name = “Jack”;
m_pSet->m_department = “3”;
m_pSet->m_age = “20”;
m_pSet->m_comment = “good”;
//更新記錄集,將新記錄存入資料來源
m_pSet->Update();
//重新整理記錄集,並使記錄集指標回到第一條記錄
m_pSet->Requery();
}

刪除記錄

呼叫CRecordset類中的Delete函式進行刪除記錄。該函式用於刪除當前記錄集指標指向的記錄。原型如下:virtual void Delete();

一次成功的刪除後,被刪除記錄的欄位全部被設為NULL,必須呼叫Move函式移動到其他記錄上來一處被刪除的記錄。如果刪除不成功,記錄中的資料也不會被破壞。一旦移除了刪除的記錄,就再也不能返回他了。在呼叫Delete函式時,記錄集中必須有一條有效的記錄,否則會產生錯誤。。如果Delete了一條記錄,但沒有Move到另一條記錄就有進行了Delete操作,Delete會產生一個CDBException類的錯誤。

下面的程式碼在一個名為OnDelete的函式中實現了刪除當前記錄的操作:

void CSampleView::OnDelete()
{
//刪除當前記錄
m_pSet->Delete();
//重新整理記錄集
m_pSet->Requery();
}

修改記錄

CRecordset::Edit函式的作用是允許修改當前的記錄。原型如下:

virtual void Edit();

呼叫Edit之後,就可以直接重新設定當前記錄中每個欄位的值了。再重新設定之後,還需要呼叫Update函式來儲存對資料的修改。實際上,呼叫Edit之後,要被修改的值先被儲存起來。如果Edit之後沒有Update,而是移動到另一條記錄,那麼記錄以前的值被重新恢復,不對記錄作出修改。或者,呼叫了一次Edit,而對記錄也作出了修改,然後又呼叫了一次Edit,那麼記錄還是被恢復到第一次呼叫Edit之前的值。

下面的程式碼在名為OnEdit的函式中實現對資料的編輯:

void CSampleView::OnEdit()
{
m_pSet->Edit();
m_pSet->m_id = m_newid;
m_pSet->m_name = m_newname;
m_pSet->m_department = m_newdepartment;
m_pSet->m_age = m_newage;
m_pSet->m_comment = m_newcomment;
m_pSet->Update();
m_pSet->Requery();
}

直接執行SQL語句(增加、刪除表等)

並不是所有的ODBC功能都被資料庫類所支援,所以有時候需要使用直接執行SQL語句來對資料庫進行一些操作。

CDatabase類中有一個函式ExecuteSQL,通過它可以直接執行SQL語句,對資料庫進行操作。原型如下:void ExecuteSQL(LPCTSTR lpszSQL);

引數lpszSQL是一個CString型別的指標,包含一條可執行的、有效地SQL命令。這個函式並不返回資訊,如果要對記錄進行操作,那麼還是要使用記錄集物件。

下面的程式碼可以在一個名為class的表中增加一條記錄。m_database是一個CDatabase類的物件。VALUES中是新增記錄的具體值,分別於表中的每一個欄位相對應。

m_pSet->Open();
m_pSet->AddNew();
m_database.ExecuteSQL(“insert into class VALUES(‘006’,’Mary’,’19’,’FEMALE’)”);
m_pSet.Update();

m_pSet->Edit();
m_database.ExecuteSQL(“Delete from class WHERE Name = ‘Mary’”);
m_pSet.Update();

事務處理

事務操作涉及CDatabase類的幾個成員函式,BeginTrans表示開始事務,CommitTrans表示接受所有對資料來源的修改,或者呼叫Rollback來終止整個事務。執行事務的三個步驟:

  • 呼叫CDatabase類物件的BeginTrans成員函式開始事務
  • 呼叫AddNew->Update、Edit->Update、Delete等函式對同一資料庫的一個或者多個記錄集進行一系列的新增、修改、刪除操作
  • 呼叫CDatabase類的CommitTrans函式執行所有新增、修改、刪除操作。如果一次更新出現錯誤,或者決定取消那些操作,呼叫Rollback函式。

下面的程式碼中要刪除一家商店中的一項商品,因為要將兩個記錄集中涉及該商品的所有記錄同時刪除,所以要使用事務處理。記錄集orderset為訂單記錄,記錄集goodsset為商品品種記錄。m_shop為CDatabase類物件,假設它已經連線到了資料來源。strgoodsID為使用者輸入的要刪除商品的ID。兩個記錄集中都使用m_strFilter變數來過濾出要刪除的記錄。

BOOL CShopDoc::RemoveGoods(CString strgoodsID)
{ //開始事務
if(!m_shop.BeginTrans())
return FALSE;
//建立訂單記錄集
COrderSet orderset(&m_shop);
orderset.m_strFilter = “goodsID =” + strgoodsID;
if(!orderset.Open(CRecordset::dynaset))
return FALSE;
//建立商品記錄集
CGoodsSet goodsset(&m_shop);
goodsset.m_strFilter = “goodsID =” + strgoodsID;
if(!goodsset.Open(CRecordset::dynaset))
return FALSE;
//操作中使用了try…catch()來捕獲錯誤資訊
//因為有時會經常出現一些意想不到的錯誤
TRY
{ //刪除訂單記錄
while(!orderSet.IsEOF())
{
orderset.Delete();
orderset.MoveNext();
}
//刪除商品條目
goodsset.Delete();
//執行事務
m_shop.CommitTrans();
}
CATCH_ALL(e)
{ //取消事務
m_shop.Rollback();
return FALSE;
}
END_CATCH_ALL
//關閉記錄集
orderset.Close();
goodsset.Close();
return TRUE;
}

使用多記錄集

有時候需要使用多個記錄集,以使在一個程式中可以操作多個表。生成工程時,通常只選擇一個表作為記錄集要對應的物件。而且就算在生成工程時選擇多個表,那麼AppWizard也只會產生多個表的一個笛卡爾乘積,並不會為每一個表產生一個記錄集。

通過Visual C++ 6.0的ClassWizard來為所需要的每個表建立一個與之對應的記錄集。

“View” à “ClassWizard” à “Add Class” à “New” à “New Class 對話方塊”填入Name,”Base Class”中選擇CRecordset 單擊”OK” à “Database Options”選擇ODBC作為資料來源,在下拉框中選擇已註冊的某個資料來源,Recordset type選擇dynaset,單擊”OK” à “Select Database Tables對話方塊”,在其中選擇想要訪問的表後單擊”OK”。這樣一個CRecordset類的派生類就建立好了。在需要使用該派生類的類中#include “***.h”(該派生類的同文件)

轉自:http://blog.sina.com.cn/s/blog_4e1e357d0100p342.html