Apple开发_数据库FMDB操作

1、获取操作对象、获取\创建数据库

  • 1.1 获取操作对象、获取数据库

    • CHFmdb.h
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    // 数据库管理器
    #define CHDbMgr   [CHFmdb shareInstance].fmdb
    // 数据库管理队列,该对象支持多线程操作
    #define CHDbQueue   [CHFmdb shareInstance].queue
    
    @interface CHFmdb : NSObject
    
    /** 数据库管理器 */
    @property(nonatomic, strong) FMDatabase *fmdb;
    /** 数据库管理队列,该对象支持多线程操作 */
    @property(nonatomic, strong) FMDatabaseQueue *queue;
    
    + (instancetype)shareInstance;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    • CHFmdb.m
      • 根据原数据库文件,拷贝数据库到沙盒文件夹,得到应用内部的数据库文件。这种方式通常应用表现在:提供电子书内容源文件用于展示。
    #import "CHFmdb.h"
    
    @interface CHFmdb ()
    
    @end
    
    #define DataBaseName   @"OutLibrary"
    
    @implementation CHFmdb
    
    + (instancetype)shareInstance {
        static CHFmdb *fmdbMgr = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            fmdbMgr = [[self alloc] init];
    
            // 判断是否存在数据库,如果有,就继续,如果没有就将工程里面的复制到Document
            // 获取所有文档路径
            NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            // 获取第一个路径
            NSString *docPath = [searchPaths objectAtIndex:0];
            // 拼接完整路径
            NSString *dbPath = [docPath stringByAppendingPathComponent:@"BookLibrary.db"];
    
            // 创建文件管理器
            NSFileManager *fileManager = [[NSFileManager alloc] init];
            // 判断该路径上是否存在数据库
            BOOL isExit = [fileManager fileExistsAtPath:dbPath];
            // 如果不存在,讲工程里面数据库的复制到Document里面
            if (!isExit) {
                // 获取工程里面的数据库路径
                NSString *bundleDBPath = [[NSBundle mainBundle] pathForResource:DataBaseName ofType:@"db"];
                // 拷贝工程里面的数据库到沙盒
                BOOL success = [fileManager copyItemAtPath:bundleDBPath toPath:dbPath error:nil];
                if (success) {
                    //CHLog(@"数据库复制成功");
                }
            }
    
            // 根据数据库路径创建数据库管理器
            fmdbMgr.fmdb = [[FMDatabase alloc] initWithPath:dbPath];
            // 为数据库设置缓存,提高查询效率
            [fmdbMgr.fmdb setShouldCacheStatements:YES];
            
            // 根据数据库路径创建数据库管理队列,该对象支持多线程操作
            fmdbMgr.queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
        });
        return fmdbMgr;
    }
    
    @end
    
  • 1.2 创建数据库

    • CHFmdb.m
      • 根据数据库文件路径获取数据库管理器,数据库不存在则自动根据路径创建。这种方式通常应用表现在:保存个人喜好,健康数据等。
    #import "CHFmdb.h"
    
    @interface CHFmdb ()
    
    @end
    
    @implementation CHFmdb
    
    + (instancetype)shareInstance {
        static CHFmdb *fmdbMgr = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            fmdbMgr = [[self alloc] init];
    
            // 判断是否存在数据库,如果有,就继续,如果没有就将工程里面的复制到Document
            // 获取所有文档路径
            NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            // 获取第一个路径
            NSString *docPath = [searchPaths objectAtIndex:0];
            // 拼接完整路径
            NSString *dbPath = [docPath stringByAppendingPathComponent:@"BookLibrary.db"];
    
            // 根据数据库路径创建数据库管理器
            fmdbMgr.fmdb = [[FMDatabase alloc] initWithPath:dbPath];
            // 为数据库设置缓存,提高查询效率
            [fmdbMgr.fmdb setShouldCacheStatements:YES];
            
            // 根据数据库路径创建数据库管理队列,该对象支持多线程操作
            fmdbMgr.queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
        });
        return fmdbMgr;
    }
    
    @end
    

2、FMDatabase 详细操作

  • 2.0 创建表

    • 先判断表是否存在,不存在再创建。属性修饰:字符串用->TEXT, 整型值用->INTEGER。
    • 创建 t_ReadHistory 表,属性有自动增长唯一的ID, 书本ID:bookId, 书名:bookName, 阅读时间:readTime
    if ([CHDbMgr open]) {
        NSString *sqlStr = NSStringFormat(@"CREATE TABLE IF NOT EXISTS t_ReadHistory(id INTEGER PRIMARY KEY AUTOINCREMENT, bookId INTEGER, bookName TEXT, readTime TEXT);");     
        BOOL success = [CHDbMgr executeUpdate:sqlStr];
    }
    
  • 2.1 增 insert into

    • executeUpdate:不确定的参数用?来占位(后面参数必须是oc对象,“;”代表语句结束)
      • 例如基础数据类型的要加 “@(xxx)”转成对象类型NSNumber。
      if ([CHDbMgr open]) {
          [CHDbMgr executeUpdate:@"insert into t_ReadHistory(bookId, bookName, readTime) values(?,?,?);" , @(1001), @"西游记", @"2020年06月23日"];  
      }
      
    • executeUpdateWithForamat:不确定的参数用%@,%d等来占位(参数为原始数据类型,执行语句不区分大小写)
      if ([CHDbMgr open]) {
          [CHDbMgr executeUpdateWithForamat:@"insert into t_ReadHistory(bookId, bookName, readTime) values(%ld, %@, %ld);", @(1001), @"西游记", @"2020年06月23日"]; 
       }
      
    • 参数是数组的使用方式
      if ([CHDbMgr open]) {
          [CHDbMgr executeUpdate:@"insert into t_ReadHistory (bookId, bookName, readTime) values(?, ?, ?);" withArgumentsInArray:@[@(1001), @"西游记", @"2020年06月23日"]];
      }
      
  • 2.2 删 delete

    • 不确定的参数用?来占位 (后面参数必须是oc对象,需要将int包装成OC对象)
    if ([CHDbMgr open]) {
        [CHDbMgr executeUpdate:@"delete from t_ReadHistory where bookId = ?;", @(1001)];
    }
    
    • 不确定的参数用%@,%d等来占位
    if ([CHDbMgr open]) {
        [CHDbMgr executeUpdateWithFormat:@"delete from t_ReadHistory where bookName = %@;", @"西游记"];
    }
    
  • 2.3 查 select ... from

    if ([CHDbMgr open]) {
        // FMResultSet结果集
        FMResultSet *set = [CHDbMgr executeQuery:@"select bookId, bookName, readTime from t_ReadHistory;"];
        // next 返回yes说明有数据
        if ([set next]) { 
            NSInteger bookId = [set intForColumn:@"bookId"];
            NSString *bookName = [set stringForColumn:@"bookName"];
            NSString *readTime = [set stringForColumn:@"readTime"];
        }
        else {
            CHLog(@"查询出错");
        }
    }
    
    • 查询说明
      • select命令就是查询,执行查询的方法是以-excuteQuery开头的。
      • 执行查询时,如果成功返回FMResultSet对象,错误返回nil。
      • 与执行更新相当,支持使用NSError参数。
      • 同时,你也可以使用-lastErrorCode和-lastErrorMessage获知错误信息。
    • FMResultSet获取不同数据格式的方法:
    intForColumn:
    longForColumn:
    longLongIntForColumn:
    boolForColumn:
    doubleForColumn:
    stringForColumn:
    dataForColumn:
    dataNoCopyForColumn:
    UTF8StringForColumnIndex:
    objectForColumn:
    
    • 查询符合要求的结果条数
    if ([CHDbMgr open]) {
        // 查符合条件的书的总数
        sqlStr = @"select COUNT(*) from t_ReadHistory where bookId = ?;";
        NSUInteger bookCount = [CHDbMgr intForQuery:sqlStr, @(1001)];
    }
    
  • 2.4 改 update

if ([CHDbMgr open]) {
    [CHDbMgr executeUpdate:@"update t_ReadHistory set bookName = ? where bookId = ?", @"红楼梦", @(1001)];
}
  • 2.5 删除表 drop ...

if ([CHDbMgr open]) {
    // 如果表格存在 则销毁 
    [CHDbMgr executeUpadate:@"drop BookLibrary if existst t_ReadHistory;"];
}
  • 2.6 当添加了一列数据时,如果有需要,获取自增的id:

if ([CHDbMgr open]) {
    BOOL success = [CHDbMgr executeUpdate:@"insert into t_ReadHistory(bookId, bookName, readTime) values(?,?,?);" , @(1001), @"西游记", @"2020年06月23日"];  
    if (success) {
        // 此处为刚才添加的自增id号
        NSInteger logid = CHDbMgr.lastInsertRowId;
        CHLog(@"NSInteger == %ld", logid);
    }
}

3、FMDatabaseQueue类 实现多线程操作

  • 在多个线程中同时使用一个FMDatabase实例是不明智的。现在你可以为每个线程创建一个FMDatabase对象,不要让多个线程分享同一个实例,他无法在多个线程中同事使用。否则程序会时不时崩溃或者报告异常。所以,不要初始化FMDatabase对象,然后在多个线程中使用。这时候,我们就需要使用FMDatabaseQueue来创建队列执行事务。
  • 创建表
// 会通过block传递队列中创建好的数据库给我们
[CHDbQueue inDatabase:^(FMDatabase *db) {
    // 编写需要执行的代码
    NSString *sqlStr = NSStringFormat(@"CREATE TABLE IF NOT EXISTS t_ReadHistory(id INTEGER PRIMARY KEY AUTOINCREMENT, bookId INTEGER, bookName TEXT, readTime TEXT);");     

    BOOL success = [db executeUpdate:sqlStr];
    if (success) {
        CHLog(@"创建表成功");
    }
    else {
        CHLog(@"创建表失败");
    }
}];
  • 多任务操作
// 会通过block传递队列中创建好的数据库给我们
[CHDbQueue inDatabase:^(FMDatabase *db) {
    // 编写需要执行的代码
    [db executeUpdate:@"insert into t_ReadHistory(bookId, bookName, readTime) values(?,?,?);" , @(1001), @"西游记", @"2020年06月23日"];  
    [db executeUpdate:@"insert into t_ReadHistory(bookId, bookName, readTime) values(?,?,?);" , @(1002), @"三国演义", @"2020年06月23日"];  
}];
posted @ 2020-07-11 13:14  CH520  阅读(288)  评论(0编辑  收藏  举报