一,sqlite 简介

前面写了一篇博文讲如何在 C# 中使用 ADO 访问各种数据库,在移动开发和嵌入式领域也有一个轻量级的开源关系型数据库-sqlite。它的特点是零配置(无需服务器),单磁盘文件存储数据(就像fopen一样),平台无关性,使用起来简单又高效。这些特点让其非常适合移动开发和嵌入式领域。当然,sqlite 也因其力求简单高效,也就限制了它对并发,海量数据的处理。下面,我就再接再厉,来讲讲如何在 iOS 中使用 sqlite 库和第三方封装库 FMDB,以及介绍一个 MAC 下开源的可视化 sqlite 浏览器。

本文源码:https://github.com/kesalin/iOSSnippet/tree/master/SQLiteDemo

二,在 iOS 中的使用

在 iOS 中 sqlite3 库是一套纯 C 的接口,因此很方便地就可以在 obj-c 源码中无痕使用它,而且其使用方式与用 ADO 方式操作数据库大同小异-少了创建数据库链接一环而已(因为 sqlite 没有服务器的概念也就无需链接了)。

  • 首先,需要引入 libsqlite3.0.dylib:

然后包含头文件:

#import "/usr/include/sqlite3.h"

  • 打开或创建数据库
  1. SQLITE_API int sqlite3_open(
  2. const char *filename, /* Database filename (UTF-8) */
  3. sqlite3 **ppDb /* OUT: SQLite db handle */
  4. );

使用示例:(dbPath 为 NSString *)

  1. // open database
  2. //
  3. int state = sqlite3_open([dbPath UTF8String], &database);
  4. if (state == SQLITE_OK) {
  5. DLOG(@" >> Succeed to open database. %@", dbPath);
  6. }
  7. else {
  8. DLOG(@" >> Failed to open database. %@", dbPath);
  9. }
  • 关闭数据库
  1. SQLITE_API int sqlite3_close(sqlite3 *);

上面这个接口将关闭数据库,如果当前还有事务没有提交,会先执行 rollback 操作,然后再关闭数据库。

  • 执行 sql 语句
  1. SQLITE_API int sqlite3_exec(
  2. sqlite3*, /* An open database */
  3. const char *sql, /* SQL to be evaluated */
  4. int (*callback)(void*,int,char**,char**), /* Callback function */
  5. void *, /* 1st argument to callback */
  6. char **errmsg /* Error msg written here */
  7. );

这个接口是最常用到的,几乎除了查询之外的 sql 命令都可以用它来操作,比如创建表,插入/更新/删除记录,创建/提交/回滚事务等。注意:如果 errmsg 不为 null,那么当错误发生时, sqlite 就会为错误消息分配内存,返回给调用者,调用者有责任调用 sqlite3_free 来释放这部分内存。为了方便使用,我封装了一个简单的 obj-c 方法:

  1. - (BOOL)excuteSQLWithCString:(const char *)sqlCmd
  2. {
  3. char * errorMsg;
  4. int state = sqlite3_exec(database, sqlCmd, NULL, NULL, &errorMsg);
  5. if (state == SQLITE_OK) {
  6. DLOG(@" >> Succeed to %@",
  7. [NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding]);
  8. }
  9. else {
  10. DLOG(@" >> Failed to %@. Error: %@",
  11. [NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding],
  12. [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]);
  13.  
  14. sqlite3_free(errorMsg);
  15. }
  16.  
  17. return (state == SQLITE_OK);
  18. }

下面是创建表以及事务操作的使用示例:

  1. - (void)createTable
  2. {
  3. if (database == NULL) {
  4. DLOG(@" >> Database does not open yet.");
  5. return;
  6. }
  7.  
  8. const char * sqlCmd = "create table if not exists customer (id integer primary key autoincrement, name text not null, address text, age integer)";
  9.  
  10. [self excuteSQLWithCString:sqlCmd];
  11. }
  12.  
  13. - (BOOL)beginTransaction
  14. {
  15. return [self excuteSQLWithCString:"BEGIN EXCLUSIVE TRANSACTION;"];
  16. }
  17.  
  18. - (BOOL)commit
  19. {
  20. return [self excuteSQLWithCString:"COMMIT TRANSACTION;"];
  21. }
  22.  
  23. - (BOOL)rollback
  24. {
  25. return [self excuteSQLWithCString:"ROLLBACK TRANSACTION;"];
  26. }

很简单,不是么?至于插入,更新,删除示例,请参考如下 sqlCmd:

  1. // insert
  2. NSString * sqlCmd = [NSString stringWithFormat:@"insert into customer (name, address, age) values ('%@', '%@', %d)",
  3. customer.name, customer.address, customer.age];
  4.  
  5. // update
  6. NSString * sqlCmd = [NSString stringWithFormat:@"update customer set address='%@',age=%d where name='%@'",
  7. newValue.address, newValue.age, oldValue.name];
  8.  
  9. // delete
  10. NSString * sqlCmd = [NSString stringWithFormat:@"delete from customer where name='%@'",
  11. customer.name];
  • 查询操作

查询操作稍微负责一点,需要创建查询描述(sqlite3_stmt),然后调用如下接口编译成字节程序:

  1. SQLITE_API int sqlite3_prepare_v2(
  2. sqlite3 *db, /* Database handle */
  3. const char *zSql, /* SQL statement, UTF-8 encoded */
  4. int nByte, /* Maximum length of zSql in bytes. */
  5. sqlite3_stmt **ppStmt, /* OUT: Statement handle */
  6. const char **pzTail /* OUT: Pointer to unused portion of zSql */
  7. );

注意:这里使用的是 v2 - version 2,这是 sqlite 推荐使用的版本,version 1 仅仅是为了向后兼容而保留着。

然后使用如下接口来评估的查询结果:

  1. SQLITE_API int sqlite3_step(sqlite3_stmt*);

如果该接口返回 SQLITE_ROW,表面查询到了一行记录,我们就可以以下接口从查询描述中获取我们想要的值:

  1. SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
  2. SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
  3. SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
  4. SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol);
  5. SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol);
  6. SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
  7. SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
  8. SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
  9. SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol);
  10. SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);

最后,需要通过如下接口释放先前创建的查询描述。通常,为了提高查询效率,可以把常用的查询描述缓存起来。

  1. SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);

下面就来看一个具体的使用示例:

  1. - (NSArray *)queryAllCustomers
  2. {
  3. NSMutableArray * array = [[NSMutableArray alloc] init];
  4.  
  5. const char * sqlCmd = "select name, address, age from customer";
  6. sqlite3_stmt * statement;
  7. int state = sqlite3_prepare_v2(database, sqlCmd, -1, &statement, nil);
  8. if (state == SQLITE_OK) {
  9. DLOG(@" >> Succeed to prepare statement. %@",
  10. [NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding]);
  11. }
  12.  
  13. NSInteger index = 0;
  14. while (sqlite3_step(statement) == SQLITE_ROW) {
  15. // get raw data from statement
  16. //
  17. char * cstrName = (char *)sqlite3_column_text(statement, 0);
  18. char * cstrAddress = (char *)sqlite3_column_text(statement, 1);
  19. int age = sqlite3_column_int(statement, 2);
  20.  
  21. NSString * name = [NSString stringWithCString:cstrName encoding:NSUTF8StringEncoding];
  22. NSString * address = [NSString stringWithCString:cstrAddress encoding:NSUTF8StringEncoding];
  23. KSCustomer * customer = [[KSCustomer alloc]
  24. initWith:name
  25. address:address
  26. age:age];
  27. [array addObject:customer];
  28.  
  29. DLOG(@" >> Record %d : %@ %@ %d", index++, name, address, age);
  30. }
  31.  
  32. sqlite3_finalize(statement);
  33.  
  34. DLOG(@" >> Query %d records.", [array count]);
  35. return array;
  36. }

三,MAC 下查看 sqlite db 文件的工具

MAC 下有一款不错的开源可视化 sqlite db 浏览器:SQLite Database Browser,你可以从以下链接获取:

http://sourceforge.net/projects/sqlitebrowser/

该软件运行界面如下:

四,封装 sqlite 的第三方库 FMDB

在 iOS 中直接使用 sqlite 原生 C 接口还是不那么方便,因此催生了第三方的 iOS 版封装库,其中使用比较广泛又轻量级的就是 FMDB(https://github.com/ccgus/fmdb),目前该库只有六个文件,不超过2000行代码。

使用也是非常简单,在工程中包含这六个文件:

然后包含头文件:

  1. #import "FMDatabase.h"
  2. #import "FMResultSet.h"
  3. #import "FMDatabaseAdditions.h"

就可以使用该库了:

  1. // Create database
  2. //
  3. NSString * path = [UIHUtilities configPathFor:kDatabaseFile];
  4. FMDatabase db = [[FMDatabase databaseWithPath:path] retain];
  5.  
  6. if (![db open])
  7. {
  8. DLog(@" >> Error: Failed to open database at %@", path);
  9. }
  10.  
  11. #if DEBUG
  12. db.traceExecution = TRUE;
  13. #endif
  14.  
  15. // Create tables
  16. //
  17. [db executeUpdate:@"CREATE TABLE Image (studyUid text, patientId text, seriesUid text, SOPUid text, contentDate text, modality text, patientPosition text, filepath text, thumbnailPath text)"];
  18.  
  19. // insert
  20. //
  21. BOOL retValue = [db executeUpdate:@"INSERT INTO Image (studyUid, patientId, seriesUid, SOPUid, contentDate, patientPosition, modality, filepath, thumbnailPath) VALUES (?,?,?,?,?,?,?,?,?)", image.studyUid, image.patientId, image.seriesUid, image.SOPUid, image.contentDate, image.patientPosition, image.modality, image.filepath, image.thumbnailPath];
  22.  
  23. if (!retValue)
  24. DLog(@" >> Error: Database failed to insert image %@", image);
  25.  
  26. // query
  27. //
  28. FMResultSet *rs = [db executeQuery:@"SELECT * FROM Image WHERE SOPUid = ?", SOPUid];
  29. if ([rs next])
  30. {
  31. ....
  32. }
  33.  
  34. // query count
  35. //
  36. NSInteger count = 0;
  37. FMResultSet *rs = [db executeQuery:@"SELECT COUNT(*) FROM Image WHERE seriesUid = ?", seriesUid];
  38. if ([rs next]) {
  39. count = [rs intForColumnIndex:0];
  40. }
  41.  
  42. // delete
  43. //
  44. retValue = [db executeUpdate:@"DELETE FROM Image WHERE seriesUid = ?", seriesUid];
  45. if (!retValue)
  46. DLog(@" >> Error: Database failed to delete image by seriesUid %@", seriesUid);
  47.  
  48. // release database
  49. //
  50. [db release];

sqllite相关总结的更多相关文章

  1. ELMAH入门

    简介 ELMAH(Error Logging Modules and Handlers)错误日志记录模块和处理程序,是一种应用广泛的错误日志工具是完全可插拔.它可以动态添加到一个正在运行的ASP.NE ...

  2. sqlite3 解决并发读写冲突的问题

    #include "stdafx.h" #include "sqlite3.h" #include <iostream> #include < ...

  3. 嵌入式单片机STM32应用技术(课本)

    目录SAIU R20 1 6 第1页第1 章. 初识STM32..................................................................... ...

  4. 使用C#访问SQLLite

    1.SQLLite如何集成在C#中 2.相关C#与SQLLite资源,及说明 3.简单示例

  5. 站点发布到 IIS 后,System.Data.SqlLite.dll 末找到

    近来在部署一个站点到客户的服务器 IIS 上时,打开后却出现一个错误的页面,系统提示System.Data.SqlLite.dll 末找到,在站点部署到客户的服务器之前时,在本地测试,却没有发现什么异 ...

  6. QT 读写sqllite数据库

    QT 读写sqllite数据库 分类: 技术资料2014-04-10 10:39 84人阅读 评论(0) 收藏 举报 #include <QtGui/QApplication> #incl ...

  7. SQLLite 简介

    [1] SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内 ...

  8. SQL Server数据库读取数据的DateReader类及其相关类

    之前学了几天的SQL Server,现在用C#代码连接数据库了. 需要使用C#代码连接数据库,读取数据. 涉及的类有: ConfigurationManage SqlConnection SqlCom ...

  9. MySql的相关资操作

    01-MySql的前戏   MySql的前戏 在学习Mysql之前,我们先来想一下一开始做的登录注册案例,当时我们把用户的信息保存到一个文件中: #用户名 |密码root|123321 alex|12 ...

随机推荐

  1. P3398 仓鼠找sugar 又一次血的教训

    做什么题都要注意数组的大小,不要犯下数组越界的错误(温馨(狠心)提示): 做了好多遍就是不对,原来是[20]的数组,在for下循环1——>20,神奇爆零: 链接:https://www.luog ...

  2. final的好处

    1.final关键字提高了性能.JVM和Java应用都会缓存final变量. 2.final变量可以安全的在多线程下进行共享,而不需要额外的同步开销. 3.使用final关键字,JVM会对方法,变量和 ...

  3. Unix\Linux | 总结笔记 |文件系统_shell重定向

    输入重定向< 从文件中获得命令需要的输入数据,适合数据源已经定义好,可重复使用 #显示文件test.txt的内容 cat < tesxt.txt #统计文件test.txt中的行数 单词数 ...

  4. JEECMSv9.3在金蝶Apusic中间件中无法找到首页的问题处理

    在金蝶中间件中启动JEECMS,访问首页显示"页面找不到"信息.而访问后台及其他页面均可正常访问. 经代码查找,发现前台页面的所有地址是通过"com.jeecms.cms ...

  5. 213 House Robber II 打家劫舍 II

    注意事项: 这是 打家劫舍 的延伸.在上次盗窃完一条街道之后,窃贼又转到了一个新的地方,这样他就不会引起太多注意.这一次,这个地方的所有房屋都围成一圈.这意味着第一个房子是最后一个是紧挨着的.同时,这 ...

  6. Sping Boot返回Json格式自定义

    转载请注明http://www.cnblogs.com/majianming/p/8491020.html 在写项目过程中,遇到了需要定义返回的json字段格式的问题 例如在实体属性中,我有一个字段是 ...

  7. Oracle 表-初步涉水不深(第一天)

    oracle 关系型数据库 注释:面对大型数据处理,市场占有率40%多(但是目前正往分布式转换) 故事:本来一台大型计算机才能处理的数据,美国科学家用100台家用电脑连接,成功处理了数据.. tabl ...

  8. 2017.5.20欢(bei)乐(ju)赛解题报告

    预计分数:100+20+50=first 实际分数:20+0+10=gg 水灾(sliker.cpp/c/pas) 1000MS  64MB 大雨应经下了几天雨,却还是没有停的样子.土豪CCY刚从外地 ...

  9. STM32 (基础语法)笔记

    指针遍历: char *myitoa(int value, char *string, int radix){ int i, d; int flag = 0; char *ptr = string; ...

  10. windows上把git生成的ssh key

    右键鼠标,选中 “Git Bash here”: 输入指令,创建ssh key: cd ~/.ssh/ #bash: cd: /c/Users/Administrator/.ssh/: No such ...