WM/WinCE 下访问Sqlite的Native C++封装 (CppSQLite3U的使用)
原文作者:林永坚(Jake Lin) 原文地址:http://www.cnblogs.com/procoder/archive/2009/10/19/1585733.html 原文标题:Windows Mobile下访问Sqlite的Native C++封装 背景 当前移动设备开发领域,在本地数据存储方面,Sqlite几乎成了事实标准,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.)。那么作为移动设备领域的重要一员Windows Mobile怎么可能错过Sqlite呢。 简介 Sqlite几乎成为移动设备开发领域数据存储方面的事实标准。Sqlite已经广泛被使用到Andriod,iPhone,WebOS以及Symbian等平台了,本文讲述在Windows Mobile平台下如何使用Native C++访问Sqlite,同时讲述一个封装类的实现和使用。 Sqlite源码 Sqlite源码可以到SQLite Download Page下载,我为了省事直接使用了sqlite.phxsoftware.com的在Windows Mobile下的build工程。 Sqlite的C++封装 封装我使用了Tyushkov Nikolay的封装CppSQLite3U。这里感谢egmkang的推荐。CppSQLite3U封装是对Sqlite原有纯C的api进行OO的C++的封装。主要封装以下几个类: 1. CppSQLite3DB 数据库类,用于新建数据库,打开关闭链接,执行DDL和DML。 2. CppSQLite3Statement用于执行参数化的SQL。CppSQLite3DB可以执行SQL但是不支持参数化。 3. CppSQLite3Query 用于读出执行Select后的查询结果。 4. CppSQLite3Exception用于捕捉异常。 简单明了的封装了Sqlite。 封装类的使用 使用方法源自于我对CppSQLite3U类的单元测试。见源文件的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."); } 调用CppSQLite3DB的open()函数的时候如果发现没有数据库文件就会新建一个数据库文件。Sqlite的源代码如下(见sqlite3.c): rc = openDatabase(zFilename8,ppDb,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,0); 执行DDL 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."); } 执行CppSQLite3DB的execDML()函数可以执行DDL。Sqlite3的数据类型定义和其他常见关系型数据库有很大区别,Sqlite3数据类型定义信息是和具体的数据绑定的而不是和字段定义绑定,也就是动态的,同一个字段的不同记录可以存储不同的数据类型的数据。所以在定义表的时候定义字段类型不是必须的,具体可以参考Datatypes In SQLite Version 3。 执行DML 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,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()函数不仅可以执行DDL,而且可以执行DML。同理Update和Delete语句一样。 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()函数模仿ADO.NET的SqlCommand.ExecuteScalar用于取第一条记录的一个字段的值,一般用于聚集函数的查询。 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=%Sn",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 来取出查询的结果。eof()函数判断是否结束。nextRow()移动到下一条记录。getIntField()函数和getStringField()函数为读取当前记录的特定字段的值。 使用事务 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,currentTime.wSecond); db.execDML(sqlStr); } db.execDML(L"commit transaction;"); tmEnd = time(0); char ch[255]; sprintf(ch,tmEnd-tmStart); TRACE(ch); db.close(); } catch(CppSQLite3Exception e) { db.execDML(L"rollback transaction;"); FAIL(ToString(e.errorMessage()).c_str()); } } 在Sqlite上事务的使用非常简单。通过CppSQLite3DB的execDML()函数来打开,提交和回退事务。Sqlite在事务处理上,语法层面上和MS SQL Server有点类似,默认是自动事务(AutoCommit),关于事务处理我之前写过一篇文章,有兴趣可以参考下MS SQL Server和Oracle对数据库事务处理的差异性。也可以参考BEGIN TRANSACTION。 从测试结果看,批量处理数据,显式使用事务和自动事务在处理时间上差别很大。 在insert 100条数据时,显式使用事务小于1秒钟完成,而使用自动事务的话需要4秒钟。为什么有这么大的差别,我没有仔细研究Sqlite的源码,我从通用数据库的概念来讲述,事务其中一个特性是持久性(Durability),也就是凡是提交了的事务的数据都需要持久化,需要持久化就需要写硬盘,在移动设备是flash,写永久存储设备的速度是远远慢于写内存的速度的,所以速度差异点在IO。 项目开发中使用了TDD,关于Unit Test可以参考Wince和Windows Mobile下native C++的单元测试 和Windows Mobile下使用CppUnitLite输出测试结果 。 SqliteHelper - Native C++ wrapper class for Sqlite on Windows Mobile & Wince 检查和下载最新版本链接如下 http://sqlitehelper.codeplex.com/SourceControl/ListDownloadableCommits.aspx (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- C#---ASP页面的下拉框模糊查询功能
- c# – Azure DocumentDB Decimal Truncation
- SQLite中的一个Query中的多个Select语句
- 在R中:通过布尔比较范围内的值来建立索引向量:index == c
- 解决No module named _sqlite3的问题
- 专访NoSQLFan站长:NoSQL未来发展将细分
- 以下代码做什么?
- Oracle Linux And Oracle Database 11g R2 Intsallation
- ruby-on-rails – 如何使用Twilio接收来自用户的短信并使用
- ruby-on-rails – 我怎么弄清楚为什么我的rails 3 app,使用