如何實現(xiàn)Windows Mobile下C++訪問SqlCe的封裝
在手機(jī)下,進(jìn)行數(shù)據(jù)庫開發(fā),一般都要使用輕量級數(shù)據(jù)庫。這里介紹的是如何實現(xiàn)Windows Mobile下C++訪問SqlCe的封裝。
背景
進(jìn)行Windows Mobile的應(yīng)用的開發(fā),很多時候需要對數(shù)據(jù)庫進(jìn)行訪問,存儲和讀取??墒俏④洓]有為Native C++提供象ADO.NET下的封裝,所以我自己封裝了一個SqlCe訪問類。
簡述
本文講述在Windows Mobile和Wince下,Native C++訪問SqlCe的封裝類的實現(xiàn)。由于微軟沒有為C++提供像ADO.NET的封裝,為Native C++訪問SqlCe制造了一定的難度,因此對OleDB訪問SqlCe進(jìn)行封裝,方便使用SQL語句對SqlCe進(jìn)行操作。
SqlCe訪問技術(shù)概述
ADO.NET
.NET Compact Framework逐漸成為Windows Mobile和Wince的開發(fā)的主流,微軟為CF.NET提供了ADO.NET訪問SqlCe,對SqlCe的訪問封裝在System.Data.SqlServerCe下,和其他數(shù)據(jù)庫(SQL Server, Oracle)訪問基本兼容,但是不支持DbProviderFactory。例如下面的代碼不能通過編譯,因為沒有SqlCeProviderFactory類。
DbProviderFactory factory = SqlCeProviderFactory.Instance;我之前封裝了一個ADO.NET的SqlCe訪問類,可以參考 .NET Compact Framework下SQL CE的使用 和 實現(xiàn)了SqlCeHepler的單元測試類見.NET Compact Framework下的單元測試。
OleDb
在Native的世界,可以通過OleDb來訪問SqlCe。OleDb封裝在sqlceoledb35.dll下,使用OleDb需要安裝一個Cab包如下:
C:\Program Files\Microsoft SQL Server Compact Edition\v3.5\Devices\wce500\armv4i\sqlce.repl.ppc.wce5.armv4i.CAB上述路徑根據(jù)平臺有所不同。
注意上述sqlceoledb35.dll只是支持SqlCe 3.5,微軟對不同版本的SqlCe的兼容性不是很好,可以參考 SQL Server Express和SQL Server Compact的應(yīng)用 和 .NET Campact Framework下SQL CE兼容性問題 了解SqlCe的歷史我兼容性問題。
本文后續(xù)會重點講述OleDb。
ADO
在Windows Mobile和Wince平臺,微軟官方不支持ADO。但是有一個社區(qū)實現(xiàn),可以參考 A set of ADOCE classes。我沒有試過,所以不好評論。
實現(xiàn)
下面講述如何基于OleDb實現(xiàn)對SqlCe的訪問。
文檔和Samples
微軟提供了在線文檔,可以在 Microsoft SQL Server Compact 3.5 Books Online and Samples下載。上述鏈接只是包含在線文檔,沒有提供例子程序的,所以這個名字有點騙人感情之嫌。雖然提供了在線文檔,但是文檔的質(zhì)量不高,有些細(xì)節(jié)沒講,例如調(diào)用ICommand::Execute的入口參數(shù)就沒有講,我做的時候只能自己試。
雖然上面的鏈接不提供Samples,但是我們還有另外一個方法取得Samples。在
C:\Program Files\Microsoft SQL Server 2005 Mobile Edition\Samples\NorthwindOleDb下可以得到Sample代碼,前提是安裝了Microsoft SQL Server 2005 Mobile Edition。但是要注意不同SqlCe版本實現(xiàn)有所不同,這個代碼要結(jié)合上面的Online book修改才行。
基于SQL語句的實現(xiàn)
微軟的例子程序NorthwindOleDb提供了通過OleDb訪問SqlCe的方法,但是不提供對SQL語句的封裝,直接對表的訪問效率比較高,但是也有很大的不便性。因為每個操作都單獨(dú)封裝一個函數(shù),每個函數(shù)大概至少需要100行代碼,而且有指針等資源的分配,釋放等操作,一旦沒有處理好就發(fā)生內(nèi)存泄漏了。所以我偏向于使用統(tǒng)一的SQL語句接口對SqlCe進(jìn)行操作,雖然犧牲了點性能,卻提供了方便性。下面是當(dāng)前版本的封裝類。這個類是基于João Paulo Figueira 的OleDbClientLib3實現(xiàn)的。
打開關(guān)閉
- bool Open(const CString& dbPath);
- void Close();
上面為打開和關(guān)閉SqlCe數(shù)據(jù)庫鏈接的操作,這里實現(xiàn)為長鏈接,也就是不調(diào)用Close()的話,數(shù)據(jù)庫的Session一直鏈接著,這樣的好處是縮短訪問時間。
新建刪除
- bool CreateDatabase(const CString& dbPath);
- bool DeleteDatabase(const CString& dbPath);
由于SqlCe是in-process數(shù)據(jù)庫,物理上表現(xiàn)為就一個文件,可以方便新建和刪除,上述接口就是新建和刪除SqlCe數(shù)據(jù)庫,新建后自動打開數(shù)據(jù)庫的Senssion了。
增刪改操作
- int ExecuteNonQuery(const CString& sql);
把增刪改的語句調(diào)用ExecuteNonQuery()執(zhí)行就可以了。
- SqlCeHelper db;HRESULT hr = db.Open(DB_FILE_NAME);
- if(hr < 0){ CString cstr = db.GetErrorsMessage();
- char* str = new char[cstr.GetLength()+1];
- sprintf(str, "%S", cstr);
- FAIL(str);
- delete str;}CString sqlStr;for(int i=0; i<max; ++i)
- { SYSTEMTIME currentTime;
- GetLocalTime(¤tTime);
- sqlStr.Format(L"INSERT INTO T1 (F1, F2, F3) VALUES(%d, 'STR%d', '%d-%d-%d %d:%d:%d')", i, i, currentTime.wYear, currentTime.wMonth, currentTime.wDay, currentTime.wHour, currentTime.wMinute, currentTime.wSecond);
- hr = db.ExecuteNonQuery(sqlStr);
- if(hr < 0)
- { CString cstr = db.GetErrorsMessage();
- char* str = new char[cstr.GetLength()+1];
- sprintf(str, "%S", cstr);
- FAIL(str);
- delete str;
- }
- }db.Close();
事務(wù)處理
- void BeginTransaction();
- void CommitTransaction();
- void RollbackTransaction();
但是當(dāng)前版本還沒有事務(wù)支持。
查詢操作
其實這么多操作之中查詢操作的實現(xiàn)是最復(fù)雜的。
- int ExecuteReader(const CString& sql);
執(zhí)行ExecuteReader()會根據(jù)select語句初始化一個結(jié)果集,和ADO.net的實現(xiàn)不一樣,這個結(jié)果集不是直接通過DataSet或者DataReader來返回的,而是保持在內(nèi)部的IRowset的結(jié)構(gòu)里面。為了方便使用,SqlCeHelper實現(xiàn)了一個只讀,向前的數(shù)據(jù)讀取方法。
- bool IsEndOfRecordSet();void MoveNext();
- void CloseReader();
- int GetRowInt(const long ordinal);
- double GetRowDouble(const long ordinal);
- CString GetRowStr(const long ordinal);
當(dāng)調(diào)用ExecuteReader()后,可以調(diào)用IsEndOfRecordSet()判斷結(jié)果集是否為空。MoveNext()為移動到下一條記錄。GetRowInt(), GetRowDouble()和 GetRowStr()為讀取當(dāng)前記錄的數(shù)據(jù)。CloseReader()關(guān)閉結(jié)果集。
- SqlCeHelper db;
- HRESULT hr = db.Open(DB_FILE_NAME);
- if(hr < 0) {
- CString cstr = db.GetErrorsMessage();
- char* str = new char[cstr.GetLength()+1]; sprintf(str, "%S", cstr);
- FAIL(str);
- delete str;
- }
- CString sqlStr;
- sqlStr.Format(L"SELECT * FROM T1");
- hr = db.ExecuteReader(sqlStr);
- if(hr < 0) {
- CString cstr = db.GetErrorsMessage();
- char* str = new char[cstr.GetLength()+1]; sprintf(str, "%S", cstr);
- FAIL(str);
- delete str;
- }
- while(!db.IsEndOfRecordSet())
- {
- wprintf(L"F1=[%d], F2=[%s], F3=[%s]\n", db.GetRowInt(1), db.GetRowStr(2), db.GetRowStr(3));
- db.MoveNext();
- }
- db.CloseReader();
- db.Close();
上面演示整個讀取過程。
Unit Test
項目開發(fā)中使用了TDD,關(guān)于Unit Test可以參考Wince和Windows Mobile下native C++的單元測試 和 Windows Mobile下使用CppUnitLite輸出測試結(jié)果 。
TO-DO-LIST
**Support to open the database with password
**Support to specify the Open Mode
**Support to load Ole Db without COM registry.
**Support Transactions
**Support Parameters
原文標(biāo)題:Windows Mobile下Native C++訪問SqlCe的封裝
鏈接:http://www.cnblogs.com/procoder/archive/2009/09/09/1562947.html
【編輯推薦】