原創(chuàng)|其它|編輯:郝浩|2009-10-19 10:21:38.000|閱讀 917 次
概述:當(dāng)前移動(dòng)設(shè)備開(kāi)發(fā)領(lǐng)域,在本地?cái)?shù)據(jù)存儲(chǔ)方面,Sqlite幾乎成了事實(shí)標(biāo)準(zhǔn),Andriod (android.database.sqlite),iPhone (SQLite for iPhone SDK 和 FMDB for iPhone),Palm WebOS (webOS SQL Tutorial),新版本的Symbian也直接built-in Sqlite了(20 million Symbian smartphones shipped in Q3 2007 Newer versions of the SymbianOS have SQLite built in.)。那么作為移動(dòng)設(shè)備領(lǐng)域的重要一員Windows Mobile怎么可能錯(cuò)過(guò)Sqlite呢。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
當(dāng)前移動(dòng)設(shè)備開(kāi)發(fā)領(lǐng)域,在本地?cái)?shù)據(jù)存儲(chǔ)方面,Sqlite幾乎成了事實(shí)標(biāo)準(zhǔn),Andriod (),iPhone ( 和 ),Palm WebOS (),新版本的Symbian也直接built-in Sqlite了()。那么作為移動(dòng)設(shè)備領(lǐng)域的重要一員Windows Mobile怎么可能錯(cuò)過(guò)Sqlite呢。
Sqlite幾乎成立移動(dòng)設(shè)備開(kāi)發(fā)領(lǐng)域數(shù)據(jù)存儲(chǔ)方面的事實(shí)標(biāo)準(zhǔn)。Sqlite已經(jīng)廣泛被使用到Andriod,iPhone,WebOS以及Symbian等平臺(tái)了,本文講述在Windows Mobile平臺(tái)下如何使用Native C++訪問(wèn)Sqlite,同時(shí)講述一個(gè)封裝類(lèi)的實(shí)現(xiàn)和使用。
Sqlite源碼可以到 下載,我為了省事直接使用了的在Windows Mobile下的build工程。
封裝我使用了的封裝。這里感謝的推薦。封裝是對(duì)Sqlite原有純C的api進(jìn)行OO的C++的封裝。主要封裝以下幾個(gè)類(lèi):
1. CppSQLite3DB 數(shù)據(jù)庫(kù)類(lèi),用于新建數(shù)據(jù)庫(kù),打開(kāi)關(guān)閉鏈接,執(zhí)行DDL和DML。
2. CppSQLite3Statement 用于執(zhí)行參數(shù)化的SQL。CppSQLite3DB 可以執(zhí)行SQL但是不支持參數(shù)化。
3. CppSQLite3Query 用于讀出執(zhí)行Select后的查詢結(jié)果。
4. CppSQLite3Exception 用于捕捉異常。
簡(jiǎn)單明了的封裝了Sqlite。
使用方法源自于我對(duì)類(lèi)的單元測(cè)試。見(jiàn)源文件的SqliteHelperTest.h。
TEST(SqliteHelper, CreateDatabase)
{
try
{
CppSQLite3DB db;
DeleteFile(DB_FILE_NAME);
db.open(DB_FILE_NAME);
db.close();
}
catch(CppSQLite3Exception e)
{
FAIL(ToString(e.errorMessage()).c_str());
}
TRACE("Create database successful.");
}
調(diào)用CppSQLite3DB 的open()函數(shù)的時(shí)候如果發(fā)現(xiàn)沒(méi)有數(shù)據(jù)庫(kù)文件就會(huì)新建一個(gè)數(shù)據(jù)庫(kù)文件。Sqlite的源代碼如下(見(jiàn)sqlite3.c):
rc = openDatabase(zFilename8, ppDb,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
TEST(SqliteHelper, CreateTable)
{
try
{
CppSQLite3DB db;
db.open(DB_FILE_NAME);
db.execDML(L"create table T1(F1 int, F2 char(20), F3 char(20));");
db.close();
}
catch(CppSQLite3Exception e)
{
FAIL(ToString(e.errorMessage()).c_str());
}
TRACE("Create table successful.");
}
執(zhí)行CppSQLite3DB 的execDML()函數(shù)可以執(zhí)行DDL。Sqlite3的數(shù)據(jù)類(lèi)型定義和其他常見(jiàn)關(guān)系型數(shù)據(jù)庫(kù)有很大區(qū)別,Sqlite3數(shù)據(jù)類(lèi)型定義信息是和具體的數(shù)據(jù)綁定的而不是和字段定義綁定,也就是動(dòng)態(tài)的,同一個(gè)字段的不同記錄可以存儲(chǔ)不同的數(shù)據(jù)類(lèi)型的數(shù)據(jù)。所以在定義表的時(shí)候定義字段類(lèi)型不是必須的,具體可以參考 。
TEST(SqliteHelper, InsertTable)
{
try
{
CppSQLite3DB db;
db.open(DB_FILE_NAME);
CString sqlStr;
time_t tmStart, tmEnd;
tmStart = time(0);
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);
db.execDML(sqlStr);
}
tmEnd = time(0);
char ch[255];
sprintf(ch, "Insert table successful in %d seconds", tmEnd-tmStart);
TRACE(ch);
db.close();
}
catch(CppSQLite3Exception e)
{
FAIL(ToString(e.errorMessage()).c_str());
}
}
CppSQLite3DB 的execDML()函數(shù)不僅可以執(zhí)行DDL,而且可以執(zhí)行DML。同理Update和Delete語(yǔ)句一樣。
TEST(SqliteHelper, SelectScalarBeforeInsert)
{
try
{
CppSQLite3DB db;
db.open(DB_FILE_NAME);
int count = db.execScalar(L"SELECT COUNT(*) FROM T1;");
char ch[255];
sprintf(ch, "%d rows in T1 table", count);
TRACE(ch);
db.close();
}
catch(CppSQLite3Exception e)
{
FAIL(ToString(e.errorMessage()).c_str());
}
TRACE("Select scalar before insert successful.");
}
CppSQLite3DB 的execScalar()函數(shù)模仿ADO.NET的 用于取第一條記錄的一個(gè)字段的值,一般用于聚集函數(shù)的查詢。
TEST(SqliteHelper, SelectAfterInsert)
{
try
{
CppSQLite3DB db;
db.open(DB_FILE_NAME);
CppSQLite3Query q = db.execQuery(L"SELECT * FROM T1;");
std::string str;
char ch[255];
while (!q.eof())
{
sprintf(ch, "F1=%d, F2=%S, F3=%S\n", q.getIntField(0), q.getStringField(1), q.getStringField(2));
str += ch;
q.nextRow();
}
TRACE(str.c_str());
db.close();
}
catch(CppSQLite3Exception e)
{
FAIL(ToString(e.errorMessage()).c_str());
}
}
查詢需要借助CppSQLite3Query 來(lái)取出查詢的結(jié)果。eof()函數(shù)判斷是否結(jié)束。nextRow()移動(dòng)到下一條記錄。getIntField()函數(shù)和getStringField()函數(shù)為讀取當(dāng)前記錄的特定字段的值。
TEST(SqliteHelper, InsertTableWithTransaction)
{
try
{
CppSQLite3DB db;
db.open(DB_FILE_NAME);
CString sqlStr;
time_t tmStart, tmEnd;
tmStart = time(0);
db.execDML(L"begin transaction;");
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);
db.execDML(sqlStr);
}
db.execDML(L"commit transaction;");
tmEnd = time(0);
char ch[255];
sprintf(ch, "Insert table successful in %d seconds", tmEnd-tmStart);
TRACE(ch);
db.close();
}
catch(CppSQLite3Exception e)
{
db.execDML(L"rollback transaction;");
FAIL(ToString(e.errorMessage()).c_str());
}
}
在Sqlite上事務(wù)的使用非常簡(jiǎn)單。通過(guò)CppSQLite3DB 的execDML()函數(shù)來(lái)打開(kāi),提交和回退事務(wù)。Sqlite在事務(wù)處理上,語(yǔ)法層面上和MS SQL Server有點(diǎn)類(lèi)似,默認(rèn)是自動(dòng)事務(wù)(AutoCommit),關(guān)于事務(wù)處理我之前寫(xiě)過(guò)一篇文章,有興趣可以參考下 。也可以參考
。 從測(cè)試結(jié)果看,批量處理數(shù)據(jù),顯式使用事務(wù)和自動(dòng)事務(wù)在處理時(shí)間上差別很大。
在insert 100條數(shù)據(jù)時(shí),顯式使用事務(wù)小于1秒鐘完成,而使用自動(dòng)事務(wù)的話需要4秒鐘。為什么有這么大的差別,我沒(méi)有仔細(xì)研究Sqlite的源碼,我從通用數(shù)據(jù)庫(kù)的概念來(lái)講述,事務(wù)其中一個(gè)特性是持久性(Durability),也就是凡是提交了的事務(wù)的數(shù)據(jù)都需要持久化,需要持久化就需要寫(xiě)硬盤(pán),在移動(dòng)設(shè)備是flash,寫(xiě)永久存儲(chǔ)設(shè)備的速度是遠(yuǎn)遠(yuǎn)慢于寫(xiě)內(nèi)存的速度的,所以速度差異點(diǎn)在IO。
項(xiàng)目開(kāi)發(fā)中使用了TDD,關(guān)于Unit Test可以參考 和 。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:博客園