分享

iOS开发中的sqlite数据库

 清水之灵 2014-12-24

前面写了一篇博文讲如何在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. else { 
  7.     DLOG(@" >> Failed to open database. %@", dbPath); 

关闭数据库

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

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

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

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

  1. // insert 
  2.     NSString * sqlCmd = [NSString stringWithFormat:@"insert into customer (name, address, age) values ('%@', '%@', %d)"
  3.                            customer.name, customer.address, customer.age]; 
  4. // update 
  5.     NSString * sqlCmd = [NSString stringWithFormat:@"update customer set address='%@',age=%d where name='%@'"
  6.                          newValue.address, newValue.age, oldValue.name]; 
  7. // delete 
  8.     NSString * sqlCmd = [NSString stringWithFormat:@"delete from customer where name='%@'"
  9.                            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.     NSMutableArray * array = [[NSMutableArray alloc] init]; 
  3.     const char * sqlCmd = "select name, address, age from customer"
  4.     sqlite3_stmt * statement; 
  5.     int state = sqlite3_prepare_v2(database, sqlCmd, -1, &statement, nil); 
  6.     if (state == SQLITE_OK) { 
  7.         DLOG(@" >> Succeed to prepare statement. %@"
  8.              [NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding]); 
  9.     } 
  10.     NSInteger index = 0; 
  11.     while (sqlite3_step(statement) == SQLITE_ROW) { 
  12.         // get raw data from statement 
  13.         // 
  14.         char * cstrName = (char *)sqlite3_column_text(statement, 0); 
  15.         char * cstrAddress = (char *)sqlite3_column_text(statement, 1); 
  16.         int age = sqlite3_column_int(statement, 2); 
  17.         NSString * name = [NSString stringWithCString:cstrName encoding:NSUTF8StringEncoding]; 
  18.         NSString * address = [NSString stringWithCString:cstrAddress encoding:NSUTF8StringEncoding]; 
  19.         KSCustomer * customer = [[KSCustomer alloc] 
  20.                                  initWith:name 
  21.                                  address:address 
  22.                                  age:age]; 
  23.         [array addObject:customer]; 
  24.         DLOG(@"   >> Record %d : %@ %@ %d", index++, name, address, age); 
  25.     } 
  26.     sqlite3_finalize(statement); 
  27.     DLOG(@" >> Query %d records.", [array count]); 
  28.     return array; 

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

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

http:///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.     if (![db open]) 
  6.     {   
  7.         DLog(@" >> Error: Failed to open database at %@", path); 
  8.     } 
  9. #if DEBUG 
  10.     db.traceExecution = TRUE; 
  11. #endif 
  12.     // Create tables 
  13.     // 
  14.     [db executeUpdate:@"CREATE TABLE Image (studyUid text, patientId text, seriesUid text, SOPUid text, contentDate text, modality text, patientPosition text, filepath text, thumbnailPath text)"]; 
  15.     // insert 
  16.     // 
  17.     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];    
  18.     if (!retValue) 
  19.         DLog(@" >> Error: Database failed to insert image %@", image); 
  20.     // query 
  21.     // 
  22.     FMResultSet *rs = [db executeQuery:@"SELECT * FROM Image WHERE SOPUid = ?", SOPUid]; 
  23.     if ([rs next]) 
  24.     { 
  25.          .... 
  26.     } 
  27.     // query count 
  28.     // 
  29.     NSInteger count = 0; 
  30.     FMResultSet *rs = [db executeQuery:@"SELECT COUNT(*) FROM Image WHERE seriesUid = ?", seriesUid]; 
  31.     if ([rs next]) { 
  32.         count = [rs intForColumnIndex:0]; 
  33.     } 
  34.     // delete 
  35.     // 
  36.     retValue = [db executeUpdate:@"DELETE FROM Image WHERE seriesUid = ?", seriesUid]; 
  37.     if (!retValue) 
  38.         DLog(@" >> Error: Database failed to delete image by seriesUid %@", seriesUid); 
  39.     // release database 
  40.     // 
  41.     [db release];

怎么样,经过本文的介绍您是否对iOS开发中的sqlite数据库有了一定的理解和掌握?

【编辑推荐】

【责任编辑:闫佳明 TEL:(010)68476606】

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多