Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
Symbian 中文技术周刊
── DBMS 专题
跟踪前沿技术 深挖技术细节 致力中文推广 1
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
法律声明:本人概不承担一切因实施本文档所产生的相关责任,包括任何知识产权的侵犯,本文
档仅限于因个人学习目的的下载和打印,如需转载,请保留作者姓名,除此之外,不存在其它任
何对知识产权的授权许可.
Symbian 中文技术周刊第 3期 2004 年 12 月版
联系电话:131 8094 7386 Email:Symbiannokia@yahoo.com.cn
目 录
刊首语 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯ Peter Jiang 3
游戏高分
记录
混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载
列表的创建 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯ Peter Jiang 5
z PART Ⅰ 使用数组 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯ Peter Jiang 5
z PART Ⅱ 将高分列表保存至文件 ⋯⋯⋯⋯⋯⋯ Peter Jiang 9
z PART Ⅲ 显示 ListBox ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯ Peter Jiang 12
BookstoreDb 示例数据库引擎代码解析 ⋯⋯⋯⋯⋯⋯⋯⋯⋯ Peter Jiang 16
后记 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯ Peter Jiang 23
跟踪前沿技术 深挖技术细节 致力中文推广 2
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
刊 首 语
Happy Christmas ! 据说平安夜的晚上是要许愿的, 我不知道有没
有人许愿希望得到一期关于 Symbian OS 的 DBMS专题的电子杂志,如果有的话,我想
你应该买一个大号的袜子,然后我把装有电子杂志的光盘塞到你的袜子里.或者发送
到@袜子.com的邮箱里.废话少说,说一下搞这期杂志的目的.本来想做网络编程的,但
是苦于手里没有设备来调试,有些东西全靠翻译也不行,搞出来怕大家骂我,只有从模
拟器能实现的技术开始了.
大家请看上面这个显示gunter图的手机,是我用J2ME开发的一个
项目管理
工程项目管理制度介绍工程项目管理课程设计政府投资项目管理意见建设工程项目管理合同工程项目管理培训总结
软件
的半成品, 灵感源于我电脑中的 Project2003,预想的基本功能是项目组成员使用手机
连接至企业的 Project Server,实现实时跟踪项目开发进度,查看项目资源使用情况,
联系项目组相关人员等功能。项目的关键索引为当前日期,J2ME 对日期的支持很
糟糕,只好自己写了一个Date 类,之后比较困难的就是数据库之间的联接运算,因
为一个任务之中可能有 n个人员,因为无法确定 n的数量,只能在一个字段中存入
人员的 ID(用’/’号分隔),读入时由一个函数对其进行 Parse,而后到人员数据
库中查找并显示(诸如类似的操作还有很多)。在 PC 上一条 SQL 语句就万事大吉
的操作在 J2ME里却要写 n条。因为数据库实现繁琐,而且不了解 project 数据库内
部结构,只能凭需求猜测,写了半个月后数据库代码已是臃肿非常,本来就担心
J2ME的运行效率,另写一个浮点库的雄心壮志也灰飞烟灭(写了 CPU也运行不动
还写什么)。后又了解到 personal java,因为这类程序写完也是打算在 9210 上用的(恰
跟踪前沿技术 深挖技术细节 致力中文推广 3
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
好 9210 支持 personal java)于是学了几天 personal java,看了 sun 和 nokia 的文档却
发现都是 4年以前的,心顿时凉了半截。再加上它的运行效率及功能也不是很强,
便狠下心来直接学 Symbian 了。因为是在Microsoft 和 NOKIA 两大公司的基础上开
发,我对于移动项目管理的市场前景极其看好。但刚刚把数据库引擎写完后,在
NOKIA 软件市场上发现了同类的产品(如下图),几乎同我的设想如出一辙。于是放
弃。但通过这一过程我也对 Symbian 的数据库有了较深入的了解,直到现在我推崇
Symbian 的原因之一也是因为它强大的数据库管理系统,Symbian 的稳定性,高效的运
行效率,强大的功能,这一切都在说明着 Symbian 是行业移动信息化应用的最佳选
择.(我不是在为 Symbian 做广告,这是我开发中的切实感受)
这一期的杂志中我将翻译一篇 newlc 网站的很优秀的技术文章(也很长)。
Creation of a high score table(游戏高分记录列表的创建)。同时后面有一个我针对
NOKIA 网站上的 DBMS 的数据库代码解析。还有就是我不打算按一期技术文章一
期源代码解析的次序出版了,随心所欲的写,学到哪儿我就写到哪。让一切倒霉的
形式见鬼去吧。
Peter Jiang:男、22岁、现于长春某高校学习着和
编程毫不相关的专业,喜爱数学、长跑、跆拳道、
战争影片。今年夏天开始学习 Symbian。
跟踪前沿技术 深挖技术细节 致力中文推广 4
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
游戏高分记录列表的创建
在游戏中我们经常可以看到类似下图的分数列表,那么在 Symbian 中我们如何
来实现呢?这个问题看似简单,实际却涉及了三个问题,数组队列,文件存储,
ListBox 显示。下面我们来一起深入了解这个问题。
PART Ⅰ使用数组
数据结构描述
这一结构将以数组的形式来保存游戏者的得分,定义如下
class TScore
{
public :
TScore();
TScore(TInt aScore,const TDesC& aName);
...
public :
TInt iValue;
private:
TBuf<15> iName;
};
或许你已经注意到了 iValue不是TSore的私有成员——作为一个数据成员来说
这很少见.因为iValue将用作存储表排序的关键字,所以需要设为public.我们将在下面
的章节中了解这一内容。
TScore 对象有一个固定的长度,我们将创建 TScore 数组来控制高分列表。我
使用 CArrayFixSeg
来完成。
译者注:CarrayFixSeq是一个
模板
个人简介word模板免费下载关于员工迟到处罚通告模板康奈尔office模板下载康奈尔 笔记本 模板 下载软件方案模板免费下载
,参数为 TAny*,TAny*是一个可以包含任何
内容的内存指针,可以理解为 C++中的 void*.
CArrayFixSeq:(fix)因为 TScore 元素是定长的
CArrayFixSeq:(Sequence)便于向数组中间插入元素
CArrayFixSeq:因为数组中的内容是 TScore 元素
译者注:这一段对这一语句的英文含义做了解释。
数组定义为 CGame class 的一个 private 成员来控制我的游戏数据,定义如下:
跟踪前沿技术 深挖技术细节 致力中文推广 5
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
class CGame : public CBase
{
public :
static CGame *NewL();
static CGame *NewLC();
void ScoreDisplay();
void AddScoreL(const TScore& aScore);
void ResetScore();
~CGame();
private:
void ConstructL();
private:
CArrayFixSeg *iScoreTable;
};
创建表
在 CGame 中的二阶段构造函数中构造,在析构函数中删除
void CGame::ConstructL()
{
iScoreTable=new(ELeave)CArrayFixSeg(5);
}
CGame::~CGame()
{
delete iScoreTable;
}
表的粒度为 5,这一值的设定取决于表中元素的个数。
译者注:粒度(granularity)这一概念有些类似于 C语言中指定数组长度的值,但在 Symbian
中它是可以动态增长的,增长规律如下,假设一个数组的粒度为 4,那么当插入它的第 5个
元素时,内存中增加一个粒度空间,也就是 4+4=8.这时侯内存中富余出了三个内存空间。
如下图:
这样会涉及到一个资源浪费的问题,如果该数组只分配到第 5 个即不在向下分配,这样就
浪费了三个内存空间。具一个极端的例子来说明,如果一个数组的粒度为 100,而我们插入
了 101 条记录,这样就会浪费 99 个记录的内存。如果你说:“喔!那我把粒度都分配为 1
好了!这样就不会造成资源的浪费了!”那么你又走向了一个极端,因为每次的重新分配都
会造成不小的 CPU 及内存开销,所以正确的办法应该是根据预先估计数组可能的大小来确
定一个最优的粒度值。
向表中加入元素
我们希望通过文件来存储表中元素的。根据数据成员 iValue 的值来决定插入位
跟踪前沿技术 深挖技术细节 致力中文推广 6
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
置。
这在 Symbian 中可以通过定义 TKeyArray 非常容易的达成(在构造 TKeyArray
时指定(_FOFF(TScore,iValue)参数)。还有一个 ECmpTInt 可以让我们来对 iValue
值进行比较:
TKeyArrayFix byScore(_FOFF(TScore,iValue),ECmpTInt);
可以使用 InsertIsqAllowDuplicatesL 向 TKeyArray 插入 TScore 对象。
iScoreTable->InsertIsqAllowDuplicatesL(aScore,byScore);
当使用InsertIsqL插入一个已存在的值时会产生一个leave.这样示例中两个游戏
者就不可以使用一个相同的存储了。
这些函数只可以使用升序排序,但我们的目的是要在高分值列表中把分值由高
到低的显示。这个问题可以通过在 iValue 数据成员中存储负值来解决.:
TScore::TScore(TInt aValue,const TDesC& aName)
{
iName = aName;
iValue= -aValue;
}
这样一来,升序排列就变成了降序排列.当然,返回 iValuer 的函数也要随之改变:
TInt TScore::Score()
{
return(-iValue);
}
到这里,我们刚刚向表中加入了一个元素,但我们还没有进行任何关于表尺寸的
测试.因为移动电话的内存资源有限,我们将定义一个常量 KTableScoreSize 来对表的
尺寸进行控制,并且删除其它多余的元素。
if(iScoreTable->Count()>KTableScoreSize)
iScoreTable->Delete(KTableScoreSize);
译者注:先插入,后检测,再删除的办法,在这里我对它的控制方式不甚赞同。
我们可不可以先行检测,对于不合格的值根本不予插入,更涉及不到删除的问题,
这样无疑可以加快一些运行效率。
算法如下:
if(iScoreTable->Count()Count()= = KtableScoreSize)
//如果待插入值小于高分记录最末记录值(因为存储的是负值,即得分大于
//高分记录表中的最低分数)
if(aScore.iValueAt(KtableScoreSize).iValue)
插入;
else
不插入;
这里的判断
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
是高分记录列表的最后一个值,如果分数高于此值则插入,低于
则不插入。那么它还可不可能进一步优化,答案是可以。
跟踪前沿技术 深挖技术细节 致力中文推广 7
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
通过我们游戏的经验,达到高分是有一定难度的,不是经常有机会可以加入到高
分列表中,因此也就是大多数情况是不插入。那么我们就可以对这种不插入的情
况进行直接判断,然后就可以跳过繁杂的 if 语句了。
改为如下算法:
//如果得分小于高分记录中的最小值,即不需要插入
if(aScore.iValue>iScoreTable->At(KtableScoreSize).iValue)
不插入;
else
插入;
为什么没有了 if(iScoreTable->Count()Compress();
全部插入函数代码如下:
void CGame::AddScoreL(const TScore& aScore)
{
//
// Insert the element in the Score table
// (using an Integer key on iValue)
//
TKeyArrayFix byScore(_FOFF(TScore,iValue),ECmpTInt);
iScoreTable->InsertIsqAllowDuplicatesL(aScore,byScore);
//
// Remove the last player from the table if necessary
// and compress the table (to free unused space)
//
跟踪前沿技术 深挖技术细节 致力中文推广 8
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
if(iScoreTable->Count()>KTableScoreSize)
iScoreTable->Delete(KTableScoreSize);
iScoreTable->Compress();
}
删除元素
一个单独元素的删除可以使用上面的 Delete 语句,如果需要全部删除可以使用这条
语句.
iScoreTable->Delete(0,iScoreTable->Count());
Delete 的第一个参数为删除的起始点,第二个参数为终点.
显示表格
可以通过下面的简单 for 循环来完成:
for(i=0;iScore(name);
console->Printf(_L("%d. %05d - "),i+1,score);
console->Printf(name);
console->Printf(_L("\n"));
}
Score()为 public 类型,定义如下:
TInt Score(TPlayerName& aName);
PART Ⅱ将高分列表保存至文件
在 Symbian OS 中,有如下几个数据存储方式:
z 使用传统的文件接口.
z 使用流存储.
z 使用数据库服务.
这三组技术都可以达到我们的目的,但文件 API 不便于用来写入 C 结构的数
组.DBMS 主要提供一些较高级和复杂的存储处理操作.所以在这里我们选择直接文
件存储,这是管理 RAM内存中数据的最好选择(不能在文件系统中直接插入元素).
直接文件存储结构如下:
跟踪前沿技术 深挖技术细节 致力中文推广 9
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
Direct file stroe或以包含若干个数据流,每个数据流都可以独立进行访问,不过一
旦写入之后就不可以修改或删除.如果你想删除只能直接删除整个文件.
使用流写入/读取文件是非常方便的,而且一旦发生错误它会回滚(rollback)提交
的操作以保障文件安全(译者注:类似于 SQL 数据库中的事务处理).
将 TScore 写入 file store
需要在 TScore 类中新增两个成员函数 Externalizel()和 Internalizel():
class TScore
{
public :
TScore();
TScore(TInt aScore);
TScore(TInt aScore,const TDesC& aName);
TInt Score();
TInt Score(TPlayerName& aName);
void ExternalizeL(RWriteStream& aStream) const;
void InternalizeL(RReadStream& aStream);
TInt operator>(const TScore& aScore);
TInt operator<(const TScore& aScore);
TInt operator==(const TScore& aScore);
public :
TInt iValue;
private:
TPlayerName iName;
};
ExternalizeL()函数负责将 TScore 写入 file store
void TScore::ExternalizeL(RWriteStream& aStream) const
{
//
// Write the score as a 32bit integer
// (You have to use a WriteXXX function since no << operator
// is available for integers)
//
跟踪前沿技术 深挖技术细节 致力中文推广 10
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
aStream.WriteInt32L(iValue);
//
// Write the name
//
aStream << iName;
}
InternalizeL()函数负责从 file store 读取 TScore
void TScore::InternalizeL(RReadStream& aStream)
{
//
// Read the score
//
iValue = aStream.ReadInt32L();
//
// Read the name
//
aStream >> iName;
}
写入全部 CArray
存储表(iScoreTable)是 CGame class 的成员,负责全部数组的实例化,class 定义如
下:
class CGame : public CBase
{
public :
static CGame *NewL();
static CGame *NewLC();
void ScoreDisplay();
void AddScoreL(const TScore& aScore);
void SaveScoreL(const TDesC& aStore);
void LoadScoreL(const TDesC& aStore);
void ResetScore();
~CGame();
private:
void ConstructL();
private:
CArrayFixSeg *iScoreTable;
RFs iFs; // FileServer Session ID
};
这里仅增加了2个新函数SaveScoreL()和LoadScoreL(),和一个用于控制文件服务
的数据成员(iFs).
连接 file server 的语句在 CGame::ConstructL()函数之中,如果不能顺利连接 file
server,将产生一个 Leave,因为游戏的运行必需要它的支持.
//
// Connect to the file server and create the directory if needed
//
User::LeaveIfError(iFs.Connect());
iFs.MkDirAll(KFileStore);
MkDirAll()函数用于创建用于存储高分记录文件的目录(由KFileStore指定).这个
目录将在游戏第一次运行时建立,而后就不需要这个操作了.
SaveScoreL()函数中包含了大量工作.分以下几步完成.
创建文件的完整路径名(full path name):这一步使用 TParse 类和 Parse()函数完成
TParse parsedName;
iFs.Parse(aStoreName,parsedName);
创建一个新的空文件存储(如果已有同名文件,则替换)并以写入模式打开
跟踪前沿技术 深挖技术细节 致力中文推广 11
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
CDirectFileStore::ReplaceLC(iFs,parsedName.FullName(),EFileWrite);
设定一个需要的类型,因为Direct File Store 最简单,所以在这里我使用这种方式,
通常需要在管理 RAM内存中的数据时这是比较好的选择.
store->SetTypeL(KDirectFileStoreLayoutUid);
在存储中创建一个数据流
RStoreWriteStream stream;
TStreamId id = stream.CreateLC(*store);
向流中写入所有数据
//
// Write the number of score table entries in the store
//
TInt count= iScoreTable->Count();
stream.WriteInt32L(count);
//
// Then write each entry
//
TInt i;
for(i=0;iAt(i); // This will call the TScore::ExternalizeL method
}
最后向流及存储中提交所有改变
//
// Commit the changes to the stream
//
stream.CommitL();
CleanupStack::PopAndDestroy(); // stream id
//
// Set the stream in the store and commit the store
//
store->SetRootL(id);
store->CommitL();
CleanupStack::PopAndDestroy(); // store
以下是 SaveScore 的所有代码
void CGame::SaveScoreL(const TDesC& aStoreName)
{
//
// Create a direct file store that will contain the score table
// (Erase previous one)
//
TParse parsedName;
iFs.Parse(aStoreName,parsedName);
CFileStore* store = CDirectFileStore::ReplaceLC(iFs,parsedName.FullName(),EFileWrite);
store->SetTypeL(KDirectFileStoreLayoutUid);
//
// Create the hi-score stream
//
RStoreWriteStream stream;
TStreamId id = stream.CreateLC(*store);
//
// Write the number of score table entries in the store
//
TInt count= iScoreTable->Count();
stream.WriteInt32L(count);
//
// Then write each entry
//
TInt i;
for(i=0;iAt(i); // This will call the TScore::ExternalizeL method
跟踪前沿技术 深挖技术细节 致力中文推广 12
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
}
//
// Commit the changes to the stream
//
stream.CommitL();
CleanupStack::PopAndDestroy(); // stream id
//
// Set the stream in the store and commit the store
//
store->SetRootL(id);
store->CommitL();
CleanupStack::PopAndDestroy(); // store
}
PART Ⅲ显示 ListBox
在完成了创建和存储任务之后,我们现在将用户 Series60 的 Listbox 来显示高分
记录列表.
CAknDoubleNumberStyleListBox 是一个不错的列表框(见下图),如果你喜欢单行
的列表框,还有一个 CAknSingleNumberStyleListBox 也可以用来完成这一工作.
高分记录列表的基本控制(不涉及 UI)由 CScoreModel class 来完成的,这个 class
和 CGame 基本相同.
第 三 部 分 的 关 键 class 是 应 用 程 序 UI 容 器 (Application UI
container)----CScore5Container). 其 中 有 两 个 private 数 据 成 员 : 分 别 是 指 向
CEikTextListBox 和 CScoreModel 的指针.
class CScore5Container : public CCoeControl, MCoeControlObserver
{
public: // Constructors and destructor
void ConstructL(const TRect& aRect,CScoreModel *aScore);
~CScore5Container();
public: // New functions
void UpdateFromModel();
private:
void ReplaceScoreL(TInt aPos,TScore* aScore);
跟踪前沿技术 深挖技术细节 致力中文推广 13
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
// From CoeControl
void SizeChanged();
TInt CountComponentControls() const;
CCoeControl* ComponentControl(TInt aIndex) const;
void Draw(const TRect& aRect) const;
void HandleControlEventL(CCoeControl* aControl,
TCoeEvent aEventType);
TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,
TEventCode aType );
private: //data
CEikTextListBox *iListBox;
CScoreModel *iScore;
};
ListBox 构造
按惯例,listbox 在 container 的两阶段构造函数 ConstructL()中完成初始化.
Listbox 对象分配在堆(heap)上(译者注:也不知这句话是否多余,初学者记住,所有
C前缀的成员变量全是分配在heap上的).这里你可以定义 listbox 的绘制布局,这里我
们定义为 CAknDoubleNumberStyleListBox)
iListBox = new(ELeave)CAknDoubleNumberStyleListBox;
iListBox->SetContainerWindowL(*this);
然后从资源文件(RSS)从读取 r_akn_score_list 资源并初始化.(译者注:RSS 文件的
编写可以参照NOKIA 的 ListBox 文档)
TResourceReader rr;
iCoeEnv->CreateResourceReaderLC(rr,R_AKN_SCORE_LIST);
iListBox->ConstructFromResourceL(rr);
CleanupStack::PopAndDestroy(); //rr
创建滚动条(Scroll bar)
iListBox->CreateScrollBarFrameL(ETrue);
iListBox->ScrollBarFrame()->SetScrollBarVisibilityL(
CEikScrollBarFrame::EOff,
CEikScrollBarFrame::EAuto);
在以上步骤中,我们已经创建了 Listbox,现在可以使用 CountComponentControls()
和 Componentcontrol()来在容器中显示了.
CScore5Container::~CScore5Container()
{
delete iListBox;
}
TInt CScore5Container::CountComponentControls() const
{
return 1;
}
CCoeControl* CScore5Container::ComponentControl(TInt aIndex) const
{
switch ( aIndex )
{
case 0:
return iListBox;
default:
return NULL;
}
}
跟踪前沿技术 深挖技术细节 致力中文推广 14
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
ListBox RSS 文件定义请参照我在工具包中提供的 NOKIA 的文档。
向 ListBox 加入动态内容
在上面的章节中,我们学习了如何构造和显示 ListBox,现在可以向 ListBox 中加
入动态的内容了,有两个方式可以完成这一任务:
1.使用自定义的文本 array 提供 list model(可以使用MDesCArray)
2.使用文本数组提供默认的 list mode
因 为 CSoreModel 没 有 标准的 Listbox 格 式 的数组( ,,,所以这里我们使用第二种方式。我们在容器的 ConstructL()函数
结尾调用UpdateFromModelL()来完成这一操作。
void CScore5Container::UpdateFromModelL()
{
int i,count=iScore->Count();
for(i=0;iAt(i));
}
}
这一函数获取 CScoreModel 数组的每一条目并通过调用 ReplaceScoreL()写入
Listbox 的序号。
void CScore5Container::ReplaceScoreL(TInt aPos,TScore* aScore)
{
TBuf<20> str;
CDesCArray* array=static_cast(iListBox->Model()->ItemTextArray());
str.Format(_L("%d\t%S\t%d"),
aPos+1,
aScore->Name(),
aScore->Score());
array->InsertL(aPos,str);
array->Delete(aPos+1);
iListBox->HandleItemAdditionL();
}
获取指向 ListBox 内部模型的指针(array)
格式
pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载
化 TScore 存储中的条目
在 ListBox 模型中插入字符串
删除多余元素
请求 ListBox 控制
译者注:写到这里原作者用一句”But I was too lazy to fix it for now⋯.”结束了它的
文章,不幸的是我现在也想说这句话了,太困了啊。下期给大家一个好方法补上。
跟踪前沿技术 深挖技术细节 致力中文推广 15
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
BookstoreDb 示例数据库引擎代码解析
本来想把上面文章中的第二部分改写一下,但觉得这样一来有失原文的完整性,
另外也要对它的代码进行改写调试比较麻烦,索性把我在学习Symbian DBMS时的示
例代码拿出来和大家分享.这个DBEngine很全面,基本涉及了所有常用的数据库操作,
我的一个商业软件中的一部分就是由它改写而来的.(这一示例代码可以在 NOKIA
FORUM上下载到,随杂志带的压缩包中也有提供)
在看代码之前我们先来了解一下示例中的几个关键 class
RDbStoreDatabase:派生于RDbDatabase,提供常用的数据库处理API,独占式使用
数据库(不可共享),数据库操作直接在文件基础上执行,可以在客户端访问.另一个派
生于 RDbDatabase 的 RDbNamedDatabase 类通过数据库名称和格式打开,允许共享
(c/s 模式)使用数据库.
跟踪前沿技术 深挖技术细节 致力中文推广 16
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
CDbColSet: 数 据 库 表 格 的 列 设 定 ( 即 数 据 库 字 段 的 集 合 ), 可 以 在
RDbStoreDatabase 中创建表格.
RDbRowSet:抽象基类,提供游标导航,行检索和数据更新功能.
RDbView:派生于 RDbRowSet,标准 SQL 查询视图.
TDbQuery:字符串型的 SQL 语句封装
DBEngine.h 的前半部分说明了要使用的头文件和一些常量值,这里的待创建表
名和列名是预先定义的.
命名规则如下:
跟踪前沿技术 深挖技术细节 致力中文推广 17
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
数据库中需要命名的对象有表,索引和列.DBMS 的命名必须由字母字符
(alphabetic character)开始,之后可以跟字母字符\数字\'_'下划线字符.长度不得超过
64 个字符.
表名在数据库中必须是唯一的,同一表内的列名和索引名也必须唯一,且不
需分大小字(即 column_one==Column_ONE).
实现限制如下:
表的数量受限于设备中可用的存储空间.
表中的列数量的最大值为 8200bytes(不超过 8Kb),例如 64K 非空的 Bit 列,32
个 Text8[255]列,100 个 Text8[80]列,或 32 个 LongText8 列.
每个表最多可以有 32 个索引,索引键可以包含任意数量的主键,但唯一索引
的键总大小不得超过 248bytes,非唯一索引不得超过 244bytes.
行的数量仅受限于设备中可用的存储空间.
事务处理受限于可用的内存.
#include
#include // CDesCArrayFlat (cannot be forward declarated)
#include // RDbStoreDatabase
#include // RFs
// Forward declarations
class CFileStore;
// Constants
_LIT(KBooksTable, "Books"); //表名
_LIT(KBooksAuthorCol, "Author"); //首列
_LIT(KBooksTitleCol, "Title"); //第二列
_LIT(KBooksDescriptionCol, "Description"); //第三列
_LIT(KBooksDateCol,"PublishDate"); //可选列
_LIT(KBooksIndexName,"BooksIndex"); //索引名
const int KTitleMaxLength = 60; //列最大长度
const int KDescriptionMaxLength = 128; //最大字符串长度
/**
* Maximum length of individual item in CDesCArrayFlat (see GetAllBooksL &
* GetBooksByKeyL methods).
*/
const int KBookItemMaxLength = 256;
/**
* CDesCArrayFlat 分隔符. 格式如下:
* ||
*/
_LIT(KSeparator,"|");
注意一下其中的前三个数据成员,因为数据库是基于文件的,所以 DBMS 的实现
跟踪前沿技术 深挖技术细节 致力中文推广 18
Symbian中文技术周刊第 003期 主编:Peter Jiang Symbiannokia@yahoo.com.cn
必须依赖于 File Server,Permanent File Stores 和 Stream,这一点我们可以从构造和析构
的顺序看的很明显:
CBookstoreDb::CBookstoreDb()
{
iOpen = EFalse;
}
void CBookstoreDb::ConstructL()
{
TInt err = iFsSession.Connect();
if(err)
User::Leave(err);
}
CBookstoreDb::~CBookstoreDb()
{
Close(); // Just in case, if the user does not close this explicitely
iFsSession.Close();
}
TInt CBookstoreDb::Close()
{
iBookstoreDb.Close();
if(iFileStore)
{
delete iFileStore;
iFileStore = NULL;
}
iOpen = EFalse;
return KErrNone;
}
class CBookstoreDb : public CBase
{
public: // Creation and destruction
static CBookstoreDb* NewL();
~CBookstoreDb();
public: // Public API for database operations
TInt OpenDbL(const TFileName& aExistingBookstoreFile);
TInt CreateDbL(const TFileName& aNewBookstoreFile);
TInt RemoveDbL(const TFileName& aExistingBookstoreFile);
TInt Close();
TBool IsOpen() const;
TInt AddBookWithSqlL(const TDesC& aAut