ODBC学习(一)基本理论
1.基本理論:
一個基于ODBC的應(yīng)用程序?qū)?shù)據(jù)庫的操作不依賴任何DBMS,不直接與DBMS打交道,所有的數(shù)據(jù)庫操作由對應(yīng)的DBMS的ODBC驅(qū)動程序完成。MFC的ODBC類對較復(fù)雜的ODBC?API進行了封裝,提供了簡化的調(diào)用接口。MFC的ODBC類主要包括5個類。
?
2.MFC的ODBC類主要包括以下5個類:
1.?CDatabase類:主要功能是建立與數(shù)據(jù)源的連接
2.?CRecordset類:代表從數(shù)據(jù)源選擇的一組記錄(記錄集)
3.?CRecordView類:提供了一個表單視圖與某個記錄集直接相連,利用對話框數(shù)據(jù)交替機制(DDX)在記錄集與表單視圖的控件之間傳輸數(shù)據(jù)
4.?CFieldExchange類:支持記錄字段數(shù)據(jù)交換(RFX),即記錄集字段數(shù)據(jù)成員與相應(yīng)的數(shù)據(jù)庫的表的字段之間的數(shù)據(jù)交換。
5.?CDBException類:代表ODBC類產(chǎn)生的異常。
?
3.有如下四種類型的記錄集:
1??CRecordset::dynaset:
動態(tài)記錄集,支持雙向游標,并保持同所連接的數(shù)據(jù)源同步,對數(shù)據(jù)的更新操作可以通過一個fetch操作獲取。這種方式的好處是動態(tài)、即時的瀏覽到當前的記錄。而?其他用戶也會即時看到你對記錄所做出的修改。該選項適合于創(chuàng)建用戶要發(fā)費很多時間來編輯數(shù)據(jù)的應(yīng)用程序,并且,如果正在編寫大型數(shù)據(jù)庫應(yīng)用程序,他也是最?佳選擇
2??CRecordset::snapshot:
靜態(tài)快照,一旦形成記錄集,此后數(shù)據(jù)源的所有改變都不能體現(xiàn)在記錄集里,應(yīng)用程序必須重新進行查詢,才能獲取對數(shù)據(jù)的更新。該類型記錄集也支持雙向游標。Snapshot適用于用戶查詢信息(例如生成報表等)而不適用于數(shù)據(jù)編輯
3??CRecordset::dynamic:
同?CRecordset::dynaset記錄集相比,CRecordset::dynamic記錄還能在?fetch?操作里同步其它用戶對數(shù)據(jù)的重新排序。
4??CRecordset::forwardOnly:
除了不支持逆向游標外,其它特征同CRecordset::snapshot相同。
?
?
?
?
4.詳解CDatabase、CRecordset等類的具體介紹
CDatabase類的成員函數(shù)如下表:
| 函數(shù) | 說明 |
| CDatabase | 構(gòu)造一個對象 |
| Close | 關(guān)閉數(shù)據(jù)源連接 |
| Open | 通過一個ODBC驅(qū)動程序創(chuàng)建到數(shù)據(jù)源的連接 |
| OpenEx | 通過一個ODBC驅(qū)動程序創(chuàng)建到數(shù)據(jù)源的連接 |
| BeginTrans | 開始事務(wù) |
| BindParameters | 允許在調(diào)用CDatabase::ExecuteSQL前綁定參數(shù) |
| Cancel | 取消異步操作或第二條線程中的過程 |
| CommitTrans | 執(zhí)行事務(wù) |
| ExecuteSQL | 執(zhí)行SQL語句,不返回記錄 |
| Rollback | 回滾事務(wù),數(shù)據(jù)源返回先前的狀態(tài) |
該類的屬性屬性如下表:
| 屬性 | 說明 |
| CanTransact | 如果數(shù)據(jù)源支持事務(wù),返回非零 |
| CanUpdate | 如果CDatabase可以更新,返回非零 |
| GetBookmarkPersistence | 獲得書簽對記錄集對象的持久性 |
| GetConnect | 返回ODBC連接串 |
| GetCursorCommitBehavior | 獲得提交事務(wù)對記錄集對象的影響 |
| GetCursorRollbackBehavior | 獲得回滾事務(wù)對記錄集對象的影響 |
| GetDatabaseName | 返回當前使用的數(shù)據(jù)庫名 |
| IsOpen | 如果當前CDatabase對象連接到數(shù)據(jù)源,返回非零 |
| SetLoginTimeout | 設(shè)置數(shù)據(jù)源連接的超時數(shù)(秒為單位) |
| SetQueryTimeout | 設(shè)置查詢操作的超時數(shù)(秒為單位) |
CRecordset類的成員函數(shù)如下表:
| 成員 | 說明 |
| m_hstmt | 包含記錄集的ODBC陳述句柄,類型為HSTMT |
| m_nFields | 包含記錄集中字段數(shù)據(jù)成員的數(shù)量,類型為UNIT |
| m_nParams | 包含記錄集中參數(shù)數(shù)據(jù)成員的數(shù)量,類型為UNIT |
| m_pDatabase | 包含一個CDatabase對象指針,通過它訪問數(shù)據(jù)源 |
| m_strFilter | 包含CString對象,定義SQL中WHERE子句 |
| m_strSort | 包含CString對象,定義SQL中ORDER?BY子句 |
該類的構(gòu)造方法如下表:
| 構(gòu)造方法 | 說明 |
| Close | 關(guān)閉記錄集和與之相關(guān)的HSTMT |
| CRecordset | 構(gòu)造一個CRecordset對象 |
| Open | 通過獲得表或執(zhí)行記錄集所代表的查詢來打開記錄集 |
CRecordset類記錄集屬性如下表:
| 屬性 | 說明 |
| CanAppend | 如果新記錄可以通過Addnew添加到記錄集,返回非零 |
| CanBookmark | 如果記錄集支持書簽,返回非零 |
| CanRestart | 如果Requery可以被調(diào)用來再次運行記錄集查詢,返回非零 |
| CanScroll | 如果可以在記錄中回滾,返回非零 |
| CanTransact | 如果數(shù)據(jù)源支持事務(wù),返回非零 |
| CanUpdate | 如果記錄集可以被更新,返回非零 |
| GetODBCFieldCount | 返回記錄集中字段的數(shù)量 |
| GetRecordCount | 返回記錄集中記錄的數(shù)量 |
| GetSQL | 獲得SQL字符串 |
| GetStatus | 獲得記錄集的狀態(tài) |
| GetTableName | 獲得記錄集所屬的表名 |
| IsBOF | 如果記錄集定位在第一條記錄之前,返回非零 |
| IsDeleted | 如果記錄集定位在一條刪除的記錄,返回非零 |
| IsEOF | 如果記錄集定位在最后一條記錄之后,返回非零 |
| IsOpen | 如果調(diào)用過Open函數(shù),返回非零 |
CRecordset類更行操作如下表:
| 更新操作 | 說明 |
| AddNew | 準備增加一條新紀錄,調(diào)用Update之后完成添加 |
| CancelUpdate | 取消任何未完成的更新 |
| Delete | 從記錄集中刪除當前記錄 |
| Edit | 準備對當前記錄進行修改,調(diào)用Update后完成修改 |
| Update | 通過將新記錄或編輯的數(shù)據(jù)存入數(shù)據(jù)源來完成AddNew或Edit操作 |
CFieldExchange類處理數(shù)據(jù)交換
CFieldExchange類支持數(shù)據(jù)庫類所使用的記錄集字段交換(RFX)程式。如果使用自定義的數(shù)據(jù)類型寫數(shù)據(jù)交換程式,會使用這個類。否則不會直接使用此類。RFX在記錄集對象的字段數(shù)據(jù)成員與數(shù)據(jù)源中當前記錄的相應(yīng)字段之間交換數(shù)據(jù)。
CRecordView類顯示記錄
CRecordView對象用于在控件中顯示數(shù)據(jù)庫記錄的視圖。這種視圖是一種直接連到一個CRecordView對象的格式視圖,它從一個對話框模板創(chuàng)建資源,并將CRecordView對象的字段顯示在對話框模版的控件里。對象利用DDX和RFX機制,使窗體上的空間和記錄集的字段值之間數(shù)據(jù)移動自動化,也就是說,用戶不需要編寫一行代碼就可以完成簡單的數(shù)據(jù)庫記錄查看程序。
CDBException類處理異常
由CException類派生,以3個繼承的成員變量反映對數(shù)據(jù)庫操作時的異常:
? m_nRetCode:以ODBC返回代碼(SQL_RETURN)的形式表明造成一場的原因
? m_strError:字符串,描述造成拋出異常的錯誤原因
? m_strStateNativeOrigin:字符串,用以描述以ODBC錯誤代碼表示的異常錯誤
?
?
5.詳細編程:
CDatabase類主要是為了建立應(yīng)用程序和數(shù)據(jù)源的連接功能。應(yīng)用程序應(yīng)該先使用CDatabase類的Open()函數(shù)實現(xiàn)與ODBC數(shù)據(jù)源的連接,然后傳遞CDatabase類的指針到CRecordSet類的構(gòu)造函數(shù)中,使CRecordSet對象與原來的數(shù)據(jù)源結(jié)合起來。完成了數(shù)據(jù)源的連接之后,大量的操作集中在數(shù)據(jù)集之上。完成操作之后,先關(guān)閉所有記錄集的連接,然后關(guān)閉數(shù)據(jù)源的連接。
CDatabase::virtual?BOOL?Open(
LPCTSTR?lpszDSN,//一個數(shù)據(jù)源名,此數(shù)據(jù)源名是通過ODBC管理器注冊的。如果DSN被設(shè)定在lpszConnect里,那么lpszDSN不應(yīng)在被重新設(shè)定,lpszDSN應(yīng)設(shè)為NULL。如果沒有設(shè)定lpszConnect,而且又把lpszDSN設(shè)定為NULL,那么將出現(xiàn)一個對話框,讓用戶選擇數(shù)據(jù)源。
BOOL?bExclusive?=?FALSE,?//默認為FALSE,表示以共享方式打開數(shù)據(jù)源。當前版本的類庫不支持獨占方式,如果設(shè)定為TRUE,將失敗
BOOL?bReadOnly?=?FALSE,?//如果希望連接以只讀方式打開,不想對數(shù)據(jù)源進行更新,那么設(shè)定為TRUE,所有依靠此連接打開的記錄集全部繼承此屬性。默認值為FALSE
LPCTSTR?lpszConnect?=?_T(“ODBC;”),?//連接串。連接串可能包含數(shù)據(jù)源名、數(shù)據(jù)源中用戶的ID、密碼和其他信息。整個連接串必須以”ODBC;”開頭。”ODBC;”表示連接是一個ODBC數(shù)據(jù)源。
BOOL?bUseCursorLib?=?TRUE?//如果希望加載ODBC光標動態(tài)連接庫,設(shè)定為TRUE
);
//或者也可以顯示ODBC對話框,請用戶提供連接信息
m_pdatabase.Open(NULL);
?
CRecordSet?類的成員變量m_hstmt?代表了定義該記錄集的SQL?語句句柄,m_nFields成員變量保存了記錄集中字段的個數(shù),m_nParams?成員變量保存了記錄集所使用的參數(shù)個數(shù)。CRecordSet?的記錄集通過CDatabase?實例的指針實現(xiàn)同數(shù)據(jù)源的連接,即CRecordSet?的成員變量m_pDatabase。如果記錄集使用了WHERE?子句,m_strFilter?成員變量將保存記錄集的WHERE?子句的內(nèi)容,如果記錄集使用了?ORDER?BY?子句,m_strSort?成員變量將保存記錄集的ORDER?BY?子句的內(nèi)容。
?
CRecordSet類主要是對數(shù)據(jù)集的操作:
記錄的查詢:主要使用CRecordSet類的Open()方法和Requery()成員函數(shù)。在使用CRecordSet的類對象之前,必須使用CRecordSet的成員函數(shù)Open()來獲得有效的記錄集。一旦使用過Open()函數(shù),再次查詢時使用Requery()函數(shù)就可以了。
記錄的添加:只用AddNew()函數(shù),不過必須保證在數(shù)據(jù)庫是在允許添加的模式打開的。
記錄的刪除:調(diào)用Delete()函數(shù)完成,在使用完Delete()函數(shù)之后不用調(diào)用Update()函數(shù)了。
記錄的修改:調(diào)用Edit()函數(shù)完成,在使用過Edit()函數(shù)之后需要調(diào)用Update()函數(shù)將修改結(jié)果存入數(shù)據(jù)庫。
撤銷數(shù)據(jù)庫更新操作:如果在增加、刪除或者編輯記錄之后,想撤銷原來的操作,應(yīng)該在Update()函數(shù)之前使用Move()函數(shù)。CRecordSet::Move(AFX_MOVE_REFRESH);?該函數(shù)用于撤消增加或修改模式,并恢復(fù)在增加或修改模式之前的當前記錄。其中參數(shù)AFX_MOVE_REFRESH的值為零。
?
?
直接執(zhí)行SQL操作:雖然通過?CRecordSet?類我們可以完成大多數(shù)的數(shù)據(jù)庫查詢操作,而且在CRecordSet?類的?Open()成員函數(shù)中也可以提供SQL?語句,但有的時候我們還想進行一些其他操作,例如建立新表、刪除表、建立新的字段等等,這時就需要用到CDatabase?類的直接執(zhí)行SQL?語句的機制。通過調(diào)用CDatabase?類的?ExecuteSQL()成員函數(shù)就能夠完成SQL?語句的直接執(zhí)行,代碼如下:
BOOL?CMyDB::ExecuteSQLWithReport?(const?CString&?strSQL)
{
TRY
{
m_pMyDB->ExecuteSQL(strSQL);???//直接執(zhí)行?SQL?語句
}
CATCH(CDBException,e)
{
CString?strMsg;
strMsg.LoadString(IDS_EXECUTE_SQL_FAILED);
strMsg+=strSQL;
return?FALSE;
}
END_CATCH
return?TRUE;
}
需要注意的是,由于不同DBMS?提供的數(shù)據(jù)操作語句不盡相同,直接執(zhí)行SQL?語句可能會破壞軟件的DBMS?無關(guān)性,因此在應(yīng)用中應(yīng)當慎用此類操作。
?
使用事務(wù):
一般情況下并不需要直接使用CDatabase類型的對象,因為CRecordset類型的對象可以實現(xiàn)大多數(shù)的功能、但是在進行事務(wù)處理時,CDatabase就起到關(guān)鍵作用。CDatabase?類提供了對數(shù)據(jù)庫進行操作的函數(shù),為了執(zhí)行事務(wù)操作,通過調(diào)用CanTransact函數(shù)返回當前的?CDatabase?實例是否支持事務(wù)操作,CDatabase?類提供了?BeginTrans函數(shù),當全部數(shù)據(jù)都處理完成后,可以通過調(diào)用CommitTrans函數(shù)提交事務(wù),或者在特殊情況下通過調(diào)用Rollback函數(shù)將處理回退。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
小問題總結(jié):
1.?IsBOF和IsEOF
BOOL?IsBOF(?)?const;
返回值:
如果記錄集沒有記錄,或者你已經(jīng)向前游動到第一個記錄之前,則返回非零;否則返回0.
BOOL?IsEOF(?)?const;
返回值
如果記錄集沒有記錄,或者你已經(jīng)滾動到最后一條紀錄之后,則返回非零。否則,返回0.
?
2.Requery()
在使用snapshot模式的時候增加一條記錄,當前程序不會立即刷新記錄集,調(diào)用此函數(shù)會刷新記錄集。調(diào)用此函數(shù)之后,光標在第一個記錄集上面。Requery()有兩個重要用途:一是使記錄集能反映用戶對數(shù)據(jù)源的改變;二是按照新的過濾或排序方法查詢記錄并重新建立記錄集。
?
3.撤消操作?
如果用戶選擇了增加或者修改記錄后希望放棄當前操作,可以在調(diào)用Update()?函數(shù)之前調(diào)用:?CRecordSet::Move(AFX_MOVE_REFRESH);?
來撤消增加或修改模式,并恢復(fù)在增加或修改模式之前的當前記錄。其中的參數(shù)AFX_MOVE_REFRESH?的值為零。
?
3.單文檔向?qū)Ы忉?/span>
1.None(不要任何數(shù)據(jù)支持,今后再添加很麻煩)
2.Header?files?only(該工程需要數(shù)據(jù)庫支持,但不清楚細節(jié)時選擇。工程會添加所要求的頭文件和鏈接庫,但必須自己在創(chuàng)建數(shù)據(jù)庫類)
3.Database?view?without?file?support(表示包含數(shù)據(jù)庫頭文件和鏈接庫,并創(chuàng)建記錄集和記錄視圖,應(yīng)用程序雖有文檔支持,但不支持串行化)
4.Database?view?with?file?support(表示包含數(shù)據(jù)庫頭文件、記錄集和視圖外,程序還支持串行化)
?
4.使用對話框編程時候
CRecordset m_Recordset(&m_Database);
BOOL?rsOpen=m_Recordset.Open(CRecordset::snapshot,"select?*?from?LinkInfo?where?Name='test001'");
?
這個時候你需要從CRecordSet類派生一個自己的新類,綁定數(shù)據(jù)成員就好了
?
5.自動化配置ODBC數(shù)據(jù)源
SQLConfigDataSource();
?
?
?
問題集:
1.?SetFieldNull(NULL);函數(shù)是干嘛用的?
void?SetFieldNull(?void*?pv,?BOOL?bNull?=?TRUE?);
參數(shù):?
| pv | 指向記錄集中一字段數(shù)據(jù)成員的地址,或為NULL。如果該參數(shù)為NULL,則標記記錄集中所有字段數(shù)據(jù)成員。(C++?NULL與數(shù)據(jù)庫術(shù)語中的Null不相同,后者表示“沒有值”)。 |
| bNull | 如果要將此字段數(shù)據(jù)成員標記為沒有值(Null),則設(shè)置該參數(shù)為一個非零值。如果要將此字段數(shù)據(jù)成員標記為非Null,則設(shè)置該參數(shù)為零。 |
說明:
此成員函數(shù)用來將記錄集的一個字段數(shù)據(jù)成員標記為Null(專指沒有值)或非空。當應(yīng)用程序?qū)⒁粋€新記錄增加到一個記錄集中時,其所有字段數(shù)據(jù)成員初始都設(shè)置為Null值,并標記為“臟的”(被修改了)。當應(yīng)用程序從一個數(shù)據(jù)源中檢取一個記錄時,該記錄的列或是已有值,或是Null。
注意:
此成員函數(shù)對使用成組行檢取的記錄集是不適用的。如果你已經(jīng)實現(xiàn)了成組行檢取,則SetFieldDirty將導(dǎo)致一個失敗斷言。
如果你特別希望將當前記錄的某個字段設(shè)計為沒有值,可將bNull設(shè)置為TRUE來調(diào)用SetFieldNull函數(shù),以將該字段標記為Null。如果一個字段先前標記為Null,而現(xiàn)在應(yīng)用程序想要給它一個值,則只需要簡單地設(shè)置該字段的新值。
應(yīng)用程序不需要用SetFieldNull來刪掉Null標志。要確定該字段是否可以為Null,可以調(diào)用IsFieldNullable函數(shù)。
重點:
只能在調(diào)用Edit或AddNew之后才能調(diào)用SetFieldNull函數(shù)。使用NULL作為SetFieldNull函數(shù)的第一個參數(shù),將使該函數(shù)只應(yīng)用于outputColumns,而不作用于params。例如,調(diào)用
SetFieldNull(?NULL?);
將只設(shè)置outputColumns為NULL,而params將不受影響。
要想作用于params,應(yīng)用程序必須對所想要作用的各個param提供實際地址,例如:
SetFieldNull(?&m_strParam?);
這意味著應(yīng)用程序不能像對outputColumns一樣將所有params都設(shè)置為NULL。
注意:
當設(shè)置參數(shù)為Null時,在記錄集打開之前調(diào)用SetFieldNull將導(dǎo)致一個斷言。在這種情況下,可以調(diào)用SetParamNull。
SetFieldNull通過DoFieldExchange實現(xiàn)。
2.?CDatabase::open和CDatabase::openEx的區(qū)別?
3.什么是串行化?
它是指將對象存儲到介質(zhì)(如文件、內(nèi)存緩沖區(qū)等)中或是以二進制方式通過網(wǎng)絡(luò)傳輸。之后可以通過反串行化從這些連續(xù)的字節(jié)(byte)數(shù)據(jù)重新構(gòu)建一個與原始對象狀態(tài)相同的對象,因此在特定情況下也可以說是得到一個副本,但并不是所有情況都這樣。
4.執(zhí)行sql語句時出現(xiàn)提示”無效的描述器索引”????
?
5.執(zhí)行SQL語句時候老是出錯,一定要完成增刪改查
總結(jié)
以上是生活随笔為你收集整理的ODBC学习(一)基本理论的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ADO学习(十)DataGrid控件的使
- 下一篇: C++使用SQLite步骤及示例