SQLite之三 FMDB

简介

  1. 什么是FMDB
    1. FMDB是iOS平台的SQLite数据库框架
    2. FMDB以OC的方式封装了SQLite的C语言API
  2. FMDB的优点
    1. 使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码
    2. 对比苹果自带的Core Data框架,更加轻量级和灵活
    3. 提供了多线程安全的数据库操作方法,有效地防止数据混乱

基本使用

  1. 核心类:FMDB有三个主要的类
    1. FMDatabase
      1. 一个FMDatabase对象就代表一个单独的SQLite数据库
      2. 作用: 用来执行SQL语句
    2. FMResultSet
      1. 使用FMDatabase执行查询后的结果集
    3. FMDatabaseQueue
      1. 用于在多线程中执行多个查询或更新,它是线程安全的
  2. 打开数据库
    1. 通过指定SQLite数据库文件路径来创建FMDatabase对象

       FMDatabase *db = [FMDatabase databaseWithPath:path];
       if (![db open]) {
           NSLog(@"数据库打开失败!");
       }
      
    2. 文件路径有三种情况(后面两种都不用)

      1. 具体文件路径
        1. 如果不存在会自动创建
      2. 空字符串@””
        1. 会在临时目录(沙合中的temp文件下)创建一个空的数据库
        2. 当FMDatabase连接关闭时,数据库文件也被删除
      3. nil
        1. 会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁
  3. 执行更新
    1. 在FMDB中,除查询以外的所有操作,都称为“更新”
      1. create、drop、insert、update、delete等
    2. 使用executeUpdate:方法执行更新(包含了上面的所有操作)

       - (BOOL)executeUpdate:(NSString*)sql, ...
       - (BOOL)executeUpdateWithFormat:(NSString*)format, ...
       - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
      
    3. 示例

       [db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @20, @"Jack"]
      
  4. 执行查询
    1. 查询方法

       - (FMResultSet *)executeQuery:(NSString*)sql, ...
       - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
       - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
      
    2. 示例

      // 查询数据
      FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_student"];
      // 遍历结果集
      while ([rs next]) {
          NSString *name = [rs stringForColumn:@"name"];
          int age = [rs intForColumn:@"age"];
          double score = [rs doubleForColumn:@"score"];
      }
      

FMDatabaseQueue

  1. FMDatabase这个类是线程不安全的,如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题,因此在开发中尽量使用这个类进行数据库操作!!!
  2. 为了保证线程安全,FMDB提供方便快捷的FMDatabaseQueue类
  3. FMDatabaseQueue的创建

     FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
    
  4. 简单使用

     [queue inDatabase:^(FMDatabase *db) {
         [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
         [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
         [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];
            
         FMResultSet *rs = [db executeQuery:@"select * from t_student"];
         while ([rs next]) {
             // …
         }
     }];
    
  5. 使用事务Transaction
    1. 事务:张三给李四转100元钱
    2. 发生两件事: 1.张三账户减100 2.李四账户加100
    3. 若只执行了1,那么张三就亏100元,因此,1.2步骤要么同时成功,要么同时失败。要想达到这种效果,就应该把这两个事件放到事务中。
    4. 步骤:开启事务,然后将两个操作放到事务中。
    5. 回滚:若事务中的操作无论哪一步操作失败,都可以申请回滚,一旦回滚,之前事务中的一切操作都无效。
    6. 核心代码

       [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
           [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
           [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
           [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];
                  
           FMResultSet *rs = [db executeQuery:@"select * from t_student"];
           while ([rs next]) {
               // …
           }
       }];
      
    7. 事务回滚

       *rollback = YES;
      

代码举例

  1. 基本使用:

     #import "IWViewController.h"
     #import "FMDB.h"
        
     @interface IWViewController ()
     @property (nonatomic, strong) FMDatabase *db;
     @end
        
     @implementation IWViewController
        
     - (void)viewDidLoad
     {
         [super viewDidLoad];
            
         // 0.获得沙盒中的数据库文件名
         NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"];
            
         // 1.创建数据库实例对象
         self.db = [FMDatabase databaseWithPath:filename];
           
         // 2.打开数据库
         if ( [self.db open] ) {
             NSLog(@"数据库打开成功");
                
             // 创表
             BOOL result = [self.db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer);"];
                
             if (result) {
                 NSLog(@"创表成功");
             } else {
                 NSLog(@"创表失败");
             }
         } else {
             NSLog(@"数据库打开失败");
         }
     }
        
     - (IBAction)insert
     {
         for (int i = 0; i<40; i++) {
             NSString *name = [NSString stringWithFormat:@"rose-%d", arc4random() % 1000];
             NSNumber *age = @(arc4random() % 100 + 1);
              // executeUpdate : 不确定的参数用?来占位
             [self.db executeUpdate:@"insert into t_student (name, age) values (?, ?);", name, age];
             // executeUpdateWithFormat : 不确定的参数用%@、%d等来占位
             //[self.db executeUpdateWithFormat:@"INSERT INTO t_student (name, age) VALUES (%@, %d);", name, arc4random_uniform(40)];
         }
     }
        
     - (IBAction)update
     {
         [self.db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];
     }
        
     - (IBAction)query
     {
         // 1.查询数据
         FMResultSet *rs = [self.db executeQuery:@"select * from t_student where age > ?;", @50];
            
         // 2.遍历结果集
         while (rs.next) {
             int ID = [rs intForColumn:@"id"];
             NSString *name = [rs stringForColumn:@"name"];
             int age = [rs intForColumn:@"age"];
                
             NSLog(@"%d %@ %d", ID, name, age);
         }
     }
     @end
    
  2. FMDatabaseQueue的使用

     #import "IWViewController.h"
     #import "FMDB.h"
        
     @interface IWViewController ()
     @property (nonatomic, strong) FMDatabaseQueue *queue;
     @end
        
     @implementation IWViewController
        
     - (void)viewDidLoad{
         [super viewDidLoad];
            
         // 0.获得沙盒中的数据库文件名
         NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"];
            
         // 1.创建数据库队列(内部已经打开(创建)一个数据库了)
         self.queue = [FMDatabaseQueue databaseQueueWithPath:filename];
            
         // 2.创表
         [self.queue inDatabase:^(FMDatabase *db) {
             BOOL result = [db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer);"];
                
             if (result) {
                 NSLog(@"创表成功");
             } else {
                 NSLog(@"创表失败");
             }
         }];
     }
     //插入数据
     - (IBAction)insert
     {
         [self.queue inDatabase:^(FMDatabase *db) {
             for (int i = 0; i<40; i++) {
                 NSString *name = [NSString stringWithFormat:@"rose-%d", arc4random() % 1000];
                 NSNumber *age = @(arc4random() % 100 + 1);
                 [db executeUpdate:@"insert into t_student (name, age) values (?, ?);", name, age];
             }
         }];
     }
     //查询数据
     - (IBAction)query
     {
         [self.queue inDatabase:^(FMDatabase *db) {
             // 1.查询数据
             FMResultSet *rs = [db executeQuery:@"select * from t_student where age > ?;", @50];
                
             // 2.遍历结果集
             while (rs.next) {
                 int ID = [rs intForColumn:@"id"];
                 NSString *name = [rs stringForColumn:@"name"];
                 int age = [rs intForColumn:@"age"];
                    
                 NSLog(@"%d %@ %d", ID, name, age);
             }
         }];
     }
        
     //事物处理的方式1:(自己开启事物,添加内容,提交事物)
     - (IBAction)update
     {
         [self.queue inDatabase:^(FMDatabase *db) {
             // 1. 开启事务
             //SQL语句的开启事物: begin transaction;
             //[db executeUpdate:@"begin transaction;"];
             //FMDB 封装的语法
             [db beginTransaction];
                
             //2. 放入到事物中的内容
             //第一条
             [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];
             //第二条
             [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];
                
             //不设置主动回滚,默认是3条语句都执行完之后,若有一条没有执行成功,则默认会回滚,但是,我们也可以主动回滚(比如在执行第二条时发现有问题,主动回滚)
             //3. 第二条有问题则主动回滚(在中途执行过程中发信问题,主动回滚)
             //这里是判断语句
             if (@"发现情况不对"){
                 // 回滚事务
                 // FMDB 封装的语法
                 [db rollback];
                 //SQL语句的回滚事物rollback transaction;
                 //[db executeUpdate:@"rollback transaction;"];
             }
             //第三条
             [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];
                
             //4. 提交事务
             [db commit];
             //SQL语句的提交事物commit transaction;
             //[db executeUpdate:@"commit transaction;"];
         }];
     }
        
     //事物处理的方式2:直接使用inTransaction方法
     /*
      - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
      内部执行过程
      1. 开启事物
      2.调用block
      3. 提交事物
      */
     - (IBAction)delete
     {
         [self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
             //放入到事物中的内容
             //第一条
             [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];
             //第二条
             [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];
                 //中途主动回滚(默认3条执行完之后,任一条失败全部回滚)
     //        if (发现情况不对){
     //            // 回滚事务
     //            *rollback = YES;
     //        }
             //第三条
             [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];
         }];
     }
     @end
    
Table of Contents