【reference】



原生数据库:SQlite3

一、必备条件

在ios项目中使用sqlite需要添加  libsqlite3.dylib 库

pgsql scheme 查询文件id ios查看sql文件_pgsql scheme 查询文件id

 

二、简单介绍常用方法

sqlite3          *db, 数据库句柄,跟文件句柄FILE很类似
sqlite3_stmt      *stmt, 这个相当于ODBC的Command对象,用于保存编译好的SQL语句
sqlite3_open(),   打开数据库,没有数据库时创建。
sqlite3_exec(),   执行非查询的sql语句
Sqlite3_step(), 在调用sqlite3_prepare后,使用这个函数在记录集中移动。
Sqlite3_close(), 关闭数据库文件
还有一系列的函数,用于从记录集字段中获取数据,如
sqlite3_column_text(), 取text类型的数据。
sqlite3_column_blob(),取blob类型的数据
sqlite3_column_int(), 取int类型的数据

 

三、创建(或打开)数据库

3-1首先自定义一个方法,返回我们当前应用程序沙盒目录(也就是说希望数据库保存在哪里)

 



[cpp]  view plain  copy

 



    1. -(NSString *) dataFilePath{  
    2.   
    3.    NSArray *path =  NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    4.   
    5.     NSString *document = [path objectAtIndex:0];  
    6.   
    7. return [document stringByAppendingPathComponent:TABLENAME];//'persion.sqlite'  
    8.   
    9. }



    3-2 在指定位置 创建或打开一个数据库


     

    SQLITE_OK是sqlite3的一个常量,代表操作执行成功

     



    [java]  view plain  copy

     


    1. //SQLite3  
    2.   
    3. sqlite3 *database;  
    4. if (sqlite3_open([[self dataFilePath] UTF8String], &database)!=SQLITE_OK) {  
    5.     sqlite3_close(database);  
    6. 0, @"open database faid!");  
    7. "数据库创建失败!");  
    8. }


    【注意】由于sqlite3是基于C语言编写的,而不是纯粹的object-c,所以有关字符串,我们不能使用NSString,因为它不识别,所以只能用c语言的字符串,char*,好在Nsstring提供了转换的方法,那就是 UTF8String。


     

    pgsql scheme 查询文件id ios查看sql文件_数据库_02

    上图便是我们创建的数据库在app中的指定位置。

     

     

     

    四、创建一张表

     



    [cpp]  view plain  copy

     


    1. NSString *ceateSQL = @"CREATE TABLE IF NOT EXISTS PERSIONINFO(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, AGE INTEGER, SEX TEXT, WEIGHT INTEGER, ADDRESS TEXT)";  
    2.   
    3. char *ERROR;  
    4.   
    5. if (sqlite3_exec(database, [ceateSQL UTF8String], NULL, NULL, &ERROR)!=SQLITE_OK){  
    6.     sqlite3_close(database);  
    7. "ceate table faild!");  
    8. "表创建失败");  
    9. }

    我创建了一张名为PERSIONINFO的数据库表,其中有一个自增的ID,和NAME,AGE,SEX,WEIGTH,ADDRESS五个属性。


     

     

    五、查询表数据

     



    [cpp]  view plain  copy

     



    1. NSString *quary = @"SELECT * FROM PERSIONINFO";//SELECT ROW,FIELD_DATA FROM FIELDS ORDER BY ROW  
    2. sqlite3_stmt *stmt;  
    3. if (sqlite3_prepare_v2(database, [quary UTF8String], -1, &stmt, nil) == SQLITE_OK) {  
    4.       
    5. while (sqlite3_step(stmt)==SQLITE_ROW) {  
    6.           
    7. char *name = (char *)sqlite3_column_text(stmt, 1);  
    8.         NSString *nameString = [[NSString alloc] initWithUTF8String:name];  
    9.         self.nameTextField.text = nameString;  
    10.         [nameString release];  
    11.           
    12. int age = sqlite3_column_int(stmt, 2);  
    13. "%d",age];  
    14.           
    15. char *sex = (char *)sqlite3_column_text(stmt, 3);  
    16.         NSString *sexString = [[NSString alloc] initWithUTF8String:sex];  
    17.         self.sexTextField.text = sexString;  
    18.         [sexString release];  
    19.           
    20. int weight = sqlite3_column_int(stmt, 4);  
    21. "%d",weight];  
    22.           
    23.           
    24. char *address = (char *)sqlite3_column_text(stmt, 5);  
    25.         NSString *addressString = [[NSString alloc] initWithUTF8String:address];  
    26.         self.addressTextField.text = addressString;  
    27.         [addressString release];  
    28.           
    29.           
    30.     }  
    31.       
    32.     sqlite3_finalize(stmt);  
    33. }  
    34. //用完了一定记得关闭,释放内存  
    35. sqlite3_close(database);

    sqlite3_prepare_v2是执行查询的方法,当查询语句执行成功时,使用sqlite3_step当游标指向每一行SQLITE_ROW时,我们开始读取数据


     

    sqlite_3_column_text可以读取字符串类型的数据,参数二为column号,sqlite_3column_int读取int类型数据,其它的就不举例了,大家可以尝试。

     

     

    六、保存,插入数据

     



    [cpp]  view plain  copy

     



    1. char *update = "INSERT OR REPLACE INTO PERSIONINFO(NAME,AGE,SEX,WEIGHT,ADDRESS)""VALUES(?,?,?,?,?);";  
    2. //上边的update也可以这样写:  
    3. //NSString *insert = [NSString stringWithFormat:@"INSERT OR REPLACE INTO PERSIONINFO('%@','%@','%@','%@','%@')VALUES(?,?,?,?,?)",NAME,AGE,SEX,WEIGHT,ADDRESS];  
    4.   
    5. char *errorMsg = NULL;  
    6. sqlite3_stmt *stmt;  
    7.   
    8. if (sqlite3_prepare_v2(database, update, -1, &stmt, nil) == SQLITE_OK) {  
    9.       
    10. //【插入数据】在这里我们使用绑定数据的方法,参数一:sqlite3_stmt,参数二:插入列号,参数三:插入的数据,参数四:数据长度(-1代表全部),参数五:是否需要回调  
    11.     sqlite3_bind_text(stmt, 1, [self.nameTextField.text UTF8String], -1, NULL);  
    12.     sqlite3_bind_int(stmt, 2, [self.ageTextField.text intValue]);  
    13.     sqlite3_bind_text(stmt, 3, [self.sexTextField.text UTF8String], -1, NULL);  
    14.     sqlite3_bind_int(stmt, 4, [self.weightTextField.text integerValue]);  
    15.     sqlite3_bind_text(stmt, 5, [self.addressTextField.text UTF8String], -1, NULL);  
    16. }  
    17. if (sqlite3_step(stmt) != SQLITE_DONE)  
    18. NSLog(@"数据更新失败");  
    19. NSAssert(0, @"error updating :%s",errorMsg);  
    20.   
    21.  sqlite3_finalize(stmt);  
    22.   
    23. sqlite3_close(database);



    当然,你也可以用大家熟知的,直接把数据写在要执行的sql语句后面,如下:


     

     



    [cpp]  view plain  copy

     



      1. NSString *insert = [NSString stringWithFormat:@"INSERT OR REPLACE INTO PERSIONINFO('%@','%@','%@','%@','%@')VALUES('%@','%d','%@','%d','%@')",NAME,AGE,SEX,WEIGHT,ADDRESS,@"小杨",23,@"man",65,@"中国北京,haidian,shangdi,xinxiRoad,100014"];  
      2.   
      3. //执行语句  
      4. if (sqlite3_exec(database, [insert UTF8String], NULL, NULL, &errorMsg) != SQLITE_OK) {  
      5.     sqlite3_close(database);  
      6. }



      这样看着是不是就轻松很多了呢?


       

       

       

      七:运行效果:

      pgsql scheme 查询文件id ios查看sql文件_sqlite_03

       

      demo中我在每回打开APP时,在viewdidload里,读取数据库,如果有数据,赋值到相应的属性,当用户按下home键后,及时保存当前数据。也就是说,模拟一个游戏,当我们来电话时或是按下home键做别的事情时,一定要为用户保存当前进度和数据,不然下次打开游戏,又归零了?

       

      demo地址:

       



      SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库。iOS SDK 很早就支持了 SQLite,在使用时,只需要加入 libsqlite3.dylib 依赖以及引入 sqlite3.h 头文件即可。但是,原生的 SQLite API 在使用上相当不友好,在使用时,非常不便。于是,开源社区中就出现了一系列将 SQLite API 进行封装的库,而 FMDB (https://github.com/ccgus/fmdb) 则是开源社区中的优秀者。

      FMDB 在使用上相当方便。以下是一个简单的例子:

      NSString* docsdir = [NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
      NSString* dbpath = [docsdir stringByAppendingPathComponent:@"user.sqlite"]; 
      FMDatabase* db = [FMDatabase databaseWithPath:dbpath];
      [db open];
      FMResultSet *rs = [db executeQuery:@"select * from people"];
      while ([rs next]) {
       NSLog(@"%@ %@",
       [rs stringForColumn:@"firstname"], 
       [rs stringForColumn:@"lastname"]);
      }
      [db close];

      可以看到,使用 FMDB 后的数据库代码清晰明了,比原生的 API 优雅多了。另外,FMDB 同时兼容 ARC 和非 ARC 工程,会自动根据工程配置来调整相关的内存管理代码。

      使用说明

      该使用说明主要翻译自 fmdb 的 github 项目说明文档: https://github.com/ccgus/fmdb

      引入相关文件

      首先将 FMDB 从 github 上 clone 下来,然后将以下文件 copy 到你的工程中:

      FMDatabase.h
      FMDatabase.m
      FMDatabaseAdditions.h
      FMDatabaseAdditions.m
      FMDatabasePool.h
      FMDatabasePool.m
      FMDatabaseQueue.h
      FMDatabaseQueue.m
      FMResultSet.h
      FMResultSet.m

      建立数据库

      建立数据库只需要如下一行即可 , 当该文件不存在时,fmdb 会自己创建一个。如果你传入的参数是空串:@”” ,则 fmdb 会在临时文件目录下创建这个数据库,如果你传入的参数是 NULL,则它会建立一个在内存中的数据库。

      FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];

      打开数据库

      使用如下语句,如果打开失败,可能是权限不足或者资源不足。通常打开完操作操作后,需要调用 close 方法来关闭数据库。

      if (![db open]) {
          // error 
          return;
      }
      // some operation
      // ...
      
      [db close];

      执行更新操作

      除了 Select 操作之外,其它的都是更新操作。更新操作使用如下方法,如果有错误,可以用 error 参数中获得。

      -[FMDatabase executeUpdate:error:withArgumentsInArray:orVAList:]

      执行查询操作

      查询操作示例如下。注意:即使操作结果只有一行,也需要先调用 FMResultSet 的 next 方法。

      FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
      while ([s next]) {
          //retrieve values for each record
      }
      
      FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
      if ([s next]) {
       int totalCount = [s intForColumnIndex:0];
      }

      FMDB 提供如下多个方法来获取不同类型的数据:

      intForColumn:
      longForColumn:
      longLongIntForColumn:
      boolForColumn:
      doubleForColumn:
      stringForColumn:
      dateForColumn:
      dataForColumn:
      dataNoCopyForColumn:
      UTF8StringForColumnIndex:
      objectForColumn:

      通常情况下,你并不需要关闭 FMResultSet,因为相关的数据库关闭时,FMResultSet 也会被自动关闭。

      数据参数

      通常情况下,你可以按照标准的 SQL 语句,用 ? 表示执行语句的参数,如:

      INSERT INTO myTable VALUES (?, ?, ?)

      然后,可以我们可以调用 executeUpdate 方法来将 ? 所指代的具体参数传入,通常是用变长参数来传递进去的,如下:

      NSString *sql = @"insert into User (name, password) values (?, ?)";
      [db executeUpdate:sql, user.name, user.password];

      这里需要注意的是,参数必须是 NSObject 的子类,所以象 int,double,bool 这种基本类型,需要封装成对应的包装类才行,如下所示:

      // 错误,42 不能作为参数
      [db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];
      // 正确,将 42 封装成 NSNumber 类
      [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];

      线程安全

      如果我们的 app 需要多线程操作数据库,那么就需要使用 FMDatabaseQueue 来保证线程安全了。
      切记不能在多个线程中共同一个 FMDatabase 对象并且在多个线程中同时使用,这个类本身不是线程安全的,这样使用会造成数据混乱等问题。

      使用 FMDatabaseQueue 很简单,首先用一个数据库文件地址来初使化 FMDatabaseQueue,然后就可以将一个闭包 (block) 传入 inDatabase 方法中。
      在闭包中操作数据库,而不直接参与 FMDatabase 的管理。

      // 创建,最好放在一个单例的类中
      FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
      
      // 使用
      [queue inDatabase:^(FMDatabase *db) {
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
      
       FMResultSet *rs = [db executeQuery:@"select * from foo"];
       while ([rs next]) {
       // …
       }
      }];
      
      // 如果要支持事务
      [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
      
       if (whoopsSomethingWrongHappened) {
       *rollback = YES;
       return;
       }
       // etc…
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
      }];

      工具

      为了查看 Sqlite 中的数据,一个好的图形化界面的数据库管理程序是必不可少的。mysql 有 phpMyAdmin,那么 sqlite 呢?

      我主要使用的是 Firefox 的一个名为 SQLite Manager 的插件,安装此插件后,可以直接打开后缀名为 sqlite 的数据库文件。SQLite Manager 提供一个图形化的界面来执行数据查询或更改操作。如下图所示:

      pgsql scheme 查询文件id ios查看sql文件_pgsql scheme 查询文件id_04

      总结

      FMDB 将 SQLite API 进行了很友好的封装,使用上非常方便,对于那些使用纯 Sqlite API 来进行数据库操作的 app,可以考虑将其迁移到基于 FMDB 上,这对于以后数据库相关功能的开发维护,可以提高不少效率。

      我在学习 fmdb 的时候做了一个小工程用于练习,我把它放到 github 上了。感兴趣的可以自行下载:https://github.com/tangqiaoboy/FmdbSample

       


      转载于:


      原生数据库:SQlite3

      一、必备条件

      在ios项目中使用sqlite需要添加  libsqlite3.dylib 库

      pgsql scheme 查询文件id ios查看sql文件_pgsql scheme 查询文件id

       

      二、简单介绍常用方法

      sqlite3          *db, 数据库句柄,跟文件句柄FILE很类似
      sqlite3_stmt      *stmt, 这个相当于ODBC的Command对象,用于保存编译好的SQL语句
      sqlite3_open(),   打开数据库,没有数据库时创建。
      sqlite3_exec(),   执行非查询的sql语句
      Sqlite3_step(), 在调用sqlite3_prepare后,使用这个函数在记录集中移动。
      Sqlite3_close(), 关闭数据库文件
      还有一系列的函数,用于从记录集字段中获取数据,如
      sqlite3_column_text(), 取text类型的数据。
      sqlite3_column_blob(),取blob类型的数据
      sqlite3_column_int(), 取int类型的数据

       

      三、创建(或打开)数据库

      3-1首先自定义一个方法,返回我们当前应用程序沙盒目录(也就是说希望数据库保存在哪里)

       



      [cpp]  view plain  copy

       


      1. -(NSString *) dataFilePath{  
      2.   
      3.    NSArray *path =  NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
      4.   
      5.     NSString *document = [path objectAtIndex:0];  
      6.   
      7. return [document stringByAppendingPathComponent:TABLENAME];//'persion.sqlite'  
      8.   
      9. }



      3-2 在指定位置 创建或打开一个数据库


       

      SQLITE_OK是sqlite3的一个常量,代表操作执行成功

       



      [java]  view plain  copy

       

      1. //SQLite3  
      2.   
      3. sqlite3 *database;  
      4. if (sqlite3_open([[self dataFilePath] UTF8String], &database)!=SQLITE_OK) {  
      5.     sqlite3_close(database);  
      6. 0, @"open database faid!");  
      7. "数据库创建失败!");  
      8. }


      【注意】由于sqlite3是基于C语言编写的,而不是纯粹的object-c,所以有关字符串,我们不能使用NSString,因为它不识别,所以只能用c语言的字符串,char*,好在Nsstring提供了转换的方法,那就是 UTF8String。


       

      pgsql scheme 查询文件id ios查看sql文件_数据库_02

      上图便是我们创建的数据库在app中的指定位置。

       

       

       

      四、创建一张表

       



      [cpp]  view plain  copy

       


      1. NSString *ceateSQL = @"CREATE TABLE IF NOT EXISTS PERSIONINFO(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, AGE INTEGER, SEX TEXT, WEIGHT INTEGER, ADDRESS TEXT)";  
      2.   
      3. char *ERROR;  
      4.   
      5. if (sqlite3_exec(database, [ceateSQL UTF8String], NULL, NULL, &ERROR)!=SQLITE_OK){  
      6.     sqlite3_close(database);  
      7. "ceate table faild!");  
      8. "表创建失败");  
      9. }

      我创建了一张名为PERSIONINFO的数据库表,其中有一个自增的ID,和NAME,AGE,SEX,WEIGTH,ADDRESS五个属性。


       

       

      五、查询表数据

       



      [cpp]  view plain  copy

       



      1. NSString *quary = @"SELECT * FROM PERSIONINFO";//SELECT ROW,FIELD_DATA FROM FIELDS ORDER BY ROW  
      2. sqlite3_stmt *stmt;  
      3. if (sqlite3_prepare_v2(database, [quary UTF8String], -1, &stmt, nil) == SQLITE_OK) {  
      4.       
      5. while (sqlite3_step(stmt)==SQLITE_ROW) {  
      6.           
      7. char *name = (char *)sqlite3_column_text(stmt, 1);  
      8.         NSString *nameString = [[NSString alloc] initWithUTF8String:name];  
      9.         self.nameTextField.text = nameString;  
      10.         [nameString release];  
      11.           
      12. int age = sqlite3_column_int(stmt, 2);  
      13. "%d",age];  
      14.           
      15. char *sex = (char *)sqlite3_column_text(stmt, 3);  
      16.         NSString *sexString = [[NSString alloc] initWithUTF8String:sex];  
      17.         self.sexTextField.text = sexString;  
      18.         [sexString release];  
      19.           
      20. int weight = sqlite3_column_int(stmt, 4);  
      21. "%d",weight];  
      22.           
      23.           
      24. char *address = (char *)sqlite3_column_text(stmt, 5);  
      25.         NSString *addressString = [[NSString alloc] initWithUTF8String:address];  
      26.         self.addressTextField.text = addressString;  
      27.         [addressString release];  
      28.           
      29.           
      30.     }  
      31.       
      32.     sqlite3_finalize(stmt);  
      33. }  
      34. //用完了一定记得关闭,释放内存  
      35. sqlite3_close(database);

      sqlite3_prepare_v2是执行查询的方法,当查询语句执行成功时,使用sqlite3_step当游标指向每一行SQLITE_ROW时,我们开始读取数据


       

      sqlite_3_column_text可以读取字符串类型的数据,参数二为column号,sqlite_3column_int读取int类型数据,其它的就不举例了,大家可以尝试。

       

       

      六、保存,插入数据

       



      [cpp]  view plain  copy

       


      1. char *update = "INSERT OR REPLACE INTO PERSIONINFO(NAME,AGE,SEX,WEIGHT,ADDRESS)""VALUES(?,?,?,?,?);";  
      2. //上边的update也可以这样写:  
      3. //NSString *insert = [NSString stringWithFormat:@"INSERT OR REPLACE INTO PERSIONINFO('%@','%@','%@','%@','%@')VALUES(?,?,?,?,?)",NAME,AGE,SEX,WEIGHT,ADDRESS];  
      4.   
      5. char *errorMsg = NULL;  
      6. sqlite3_stmt *stmt;  
      7.   
      8. if (sqlite3_prepare_v2(database, update, -1, &stmt, nil) == SQLITE_OK) {  
      9.       
      10. //【插入数据】在这里我们使用绑定数据的方法,参数一:sqlite3_stmt,参数二:插入列号,参数三:插入的数据,参数四:数据长度(-1代表全部),参数五:是否需要回调  
      11.     sqlite3_bind_text(stmt, 1, [self.nameTextField.text UTF8String], -1, NULL);  
      12.     sqlite3_bind_int(stmt, 2, [self.ageTextField.text intValue]);  
      13.     sqlite3_bind_text(stmt, 3, [self.sexTextField.text UTF8String], -1, NULL);  
      14.     sqlite3_bind_int(stmt, 4, [self.weightTextField.text integerValue]);  
      15.     sqlite3_bind_text(stmt, 5, [self.addressTextField.text UTF8String], -1, NULL);  
      16. }  
      17. if (sqlite3_step(stmt) != SQLITE_DONE)  
      18. NSLog(@"数据更新失败");  
      19. NSAssert(0, @"error updating :%s",errorMsg);  
      20.   
      21.  sqlite3_finalize(stmt);  
      22.   
      23. sqlite3_close(database);


      当然,你也可以用大家熟知的,直接把数据写在要执行的sql语句后面,如下:


       

       



      [cpp]  view plain  copy

       

      1. NSString *insert = [NSString stringWithFormat:@"INSERT OR REPLACE INTO PERSIONINFO('%@','%@','%@','%@','%@')VALUES('%@','%d','%@','%d','%@')",NAME,AGE,SEX,WEIGHT,ADDRESS,@"小杨",23,@"man",65,@"中国北京,haidian,shangdi,xinxiRoad,100014"];  
      2.   
      3. //执行语句  
      4. if (sqlite3_exec(database, [insert UTF8String], NULL, NULL, &errorMsg) != SQLITE_OK) {  
      5.     sqlite3_close(database);  
      6. }

      这样看着是不是就轻松很多了呢?


       

       

       

      七:运行效果:

      pgsql scheme 查询文件id ios查看sql文件_sqlite_03

       

      demo中我在每回打开APP时,在viewdidload里,读取数据库,如果有数据,赋值到相应的属性,当用户按下home键后,及时保存当前数据。也就是说,模拟一个游戏,当我们来电话时或是按下home键做别的事情时,一定要为用户保存当前进度和数据,不然下次打开游戏,又归零了?

       

      demo地址:

       


      【reference】http://blog.devtang.com/2012/04/22/use-fmdb/

       

      SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库。iOS SDK 很早就支持了 SQLite,在使用时,只需要加入 libsqlite3.dylib 依赖以及引入 sqlite3.h 头文件即可。但是,原生的 SQLite API 在使用上相当不友好,在使用时,非常不便。于是,开源社区中就出现了一系列将 SQLite API 进行封装的库,而 FMDB (https://github.com/ccgus/fmdb) 则是开源社区中的优秀者。

      FMDB 在使用上相当方便。以下是一个简单的例子:

      NSString* docsdir = [NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
      NSString* dbpath = [docsdir stringByAppendingPathComponent:@"user.sqlite"]; 
      FMDatabase* db = [FMDatabase databaseWithPath:dbpath];
      [db open];
      FMResultSet *rs = [db executeQuery:@"select * from people"];
      while ([rs next]) {
       NSLog(@"%@ %@",
       [rs stringForColumn:@"firstname"], 
       [rs stringForColumn:@"lastname"]);
      }
      [db close];

      可以看到,使用 FMDB 后的数据库代码清晰明了,比原生的 API 优雅多了。另外,FMDB 同时兼容 ARC 和非 ARC 工程,会自动根据工程配置来调整相关的内存管理代码。

      使用说明

      该使用说明主要翻译自 fmdb 的 github 项目说明文档: https://github.com/ccgus/fmdb

      引入相关文件

      首先将 FMDB 从 github 上 clone 下来,然后将以下文件 copy 到你的工程中:

      FMDatabase.h
      FMDatabase.m
      FMDatabaseAdditions.h
      FMDatabaseAdditions.m
      FMDatabasePool.h
      FMDatabasePool.m
      FMDatabaseQueue.h
      FMDatabaseQueue.m
      FMResultSet.h
      FMResultSet.m

      建立数据库

      建立数据库只需要如下一行即可 , 当该文件不存在时,fmdb 会自己创建一个。如果你传入的参数是空串:@”” ,则 fmdb 会在临时文件目录下创建这个数据库,如果你传入的参数是 NULL,则它会建立一个在内存中的数据库。

      FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];

      打开数据库

      使用如下语句,如果打开失败,可能是权限不足或者资源不足。通常打开完操作操作后,需要调用 close 方法来关闭数据库。

      if (![db open]) {
          // error 
          return;
      }
      // some operation
      // ...
      
      [db close];

      执行更新操作

      除了 Select 操作之外,其它的都是更新操作。更新操作使用如下方法,如果有错误,可以用 error 参数中获得。

      -[FMDatabase executeUpdate:error:withArgumentsInArray:orVAList:]

      执行查询操作

      查询操作示例如下。注意:即使操作结果只有一行,也需要先调用 FMResultSet 的 next 方法。

      FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
      while ([s next]) {
          //retrieve values for each record
      }
      
      FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
      if ([s next]) {
       int totalCount = [s intForColumnIndex:0];
      }

      FMDB 提供如下多个方法来获取不同类型的数据:

      intForColumn:
      longForColumn:
      longLongIntForColumn:
      boolForColumn:
      doubleForColumn:
      stringForColumn:
      dateForColumn:
      dataForColumn:
      dataNoCopyForColumn:
      UTF8StringForColumnIndex:
      objectForColumn:

      通常情况下,你并不需要关闭 FMResultSet,因为相关的数据库关闭时,FMResultSet 也会被自动关闭。

      数据参数

      通常情况下,你可以按照标准的 SQL 语句,用 ? 表示执行语句的参数,如:

      INSERT INTO myTable VALUES (?, ?, ?)

      然后,可以我们可以调用 executeUpdate 方法来将 ? 所指代的具体参数传入,通常是用变长参数来传递进去的,如下:

      NSString *sql = @"insert into User (name, password) values (?, ?)";
      [db executeUpdate:sql, user.name, user.password];

      这里需要注意的是,参数必须是 NSObject 的子类,所以象 int,double,bool 这种基本类型,需要封装成对应的包装类才行,如下所示:

      // 错误,42 不能作为参数
      [db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];
      // 正确,将 42 封装成 NSNumber 类
      [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];

      线程安全

      如果我们的 app 需要多线程操作数据库,那么就需要使用 FMDatabaseQueue 来保证线程安全了。
      切记不能在多个线程中共同一个 FMDatabase 对象并且在多个线程中同时使用,这个类本身不是线程安全的,这样使用会造成数据混乱等问题。

      使用 FMDatabaseQueue 很简单,首先用一个数据库文件地址来初使化 FMDatabaseQueue,然后就可以将一个闭包 (block) 传入 inDatabase 方法中。
      在闭包中操作数据库,而不直接参与 FMDatabase 的管理。

      // 创建,最好放在一个单例的类中
      FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
      
      // 使用
      [queue inDatabase:^(FMDatabase *db) {
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
      
       FMResultSet *rs = [db executeQuery:@"select * from foo"];
       while ([rs next]) {
       // …
       }
      }];
      
      // 如果要支持事务
      [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
      
       if (whoopsSomethingWrongHappened) {
       *rollback = YES;
       return;
       }
       // etc…
       [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
      }];

      工具

      为了查看 Sqlite 中的数据,一个好的图形化界面的数据库管理程序是必不可少的。mysql 有 phpMyAdmin,那么 sqlite 呢?

      我主要使用的是 Firefox 的一个名为 SQLite Manager 的插件,安装此插件后,可以直接打开后缀名为 sqlite 的数据库文件。SQLite Manager 提供一个图形化的界面来执行数据查询或更改操作。如下图所示:

      pgsql scheme 查询文件id ios查看sql文件_pgsql scheme 查询文件id_04

      总结

      FMDB 将 SQLite API 进行了很友好的封装,使用上非常方便,对于那些使用纯 Sqlite API 来进行数据库操作的 app,可以考虑将其迁移到基于 FMDB 上,这对于以后数据库相关功能的开发维护,可以提高不少效率。

      我在学习 fmdb 的时候做了一个小工程用于练习,我把它放到 github 上了。感兴趣的可以自行下载:https://github.com/tangqiaoboy/FmdbSample