FMDB的简单学习与使用

FMDB封装了SQLite,是CoreData的一个不错的替代方案

2016-06-05 | 阅读

FMDB的学习与使用

FMDB是对iOS上调用SQLite数据库的封装,主要使用三个类:

  • FMDatabase : 代表一个单独的SQLite数据库,用来执行SQL语句
  • FMResultSet: 表示在FMDatabase上一次查询的结果。
  • FMDatabaseQueue : 用于线程安全的查询和更新操作。

创建数据库:

FMDatabase创建的SQLlites数据库,会生成一个数据库文件,放在应用的沙盒中。而路径有三种情况:

  • 一个文件路径的地址。如果这个地址上没有数据库文件,会自动创建一个数据库文件
  • 一个空的字符串 @"" 。一个空的数据库创建在缓存路径中,数据库文件会在数据库连接断开时删除。
  • NULL,创建一个只存放在内存中的数据库,断开数据库连接后,也会自动被销毁

一般创建方式:

// 这里的根路径是沙盒的根目录,所以沙盒目录下的/tmp就表示是沙盒下面的缓存目录
FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
if (![db open]) {
	// 一定要确保数据库正确打开,因为有可能因为资源不足或者权限问题导致打不开数据库
    NSLog(@"数据库打开失败!");
}	

使用SQLite

只使用SELECT的是查询语句,除此之外,全部是更新操作。更新操作返回值是BOOL类型,返回No表示发生异常,可以通过-lastErrorMessagelastErrorCode来查看异常信息。

查询

查询语句,调用executeQuery,返回一个FMResultSet,查询成功会返回一个对象,返回nil表示查询失败,遇到数据库的异常。

FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
// 调用next,表示指向查询结果的下一条
if ([s next]) {
	 // 然后从FMResultSet 中获取数据
    int totalCount = [s intForColumnIndex:0];
}

FMResultSet中支持以下的类型:

  • intForColumn:
  • longForColumn:
  • longLongIntForColumn:
  • boolForColumn:
  • doubleForColumn:
  • stringForColumn:
  • dateForColumn:
  • dataForColumn:
  • dataNoCopyForColumn:
  • UTF8StringForColumnName:
  • objectForColumnName:

每个方法都有一个对应的 {type}ForColumnIndex,即一个是根据column的名称查询,一个是根据行号查询。查询完成后,不需要手动调用close关闭FMResultSet

关闭

但执行完更新和查询操作后,需要调用FMDatabaseclose来关闭数据库连接。

事物

FMDatabase中,可以通过commit,rollback等方法来现实调用事物,也可以在SQL语句中声明使用事物。

多语句和批处理

通过调用executeStatements:来调用多语句和批处理:

NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);"
                 "create table bulktest2 (id integer primary key autoincrement, y text);"
                 "create table bulktest3 (id integer primary key autoincrement, z text);"
                 "insert into bulktest1 (x) values ('XXX');"
                 "insert into bulktest2 (y) values ('YYY');"
                 "insert into bulktest3 (z) values ('ZZZ');";

success = [db executeStatements:sql];

sql = @"select count(*) as count from bulktest1;"
       "select count(*) as count from bulktest2;"
       "select count(*) as count from bulktest3;";

success = [self.db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) {
    NSInteger count = [dictionary[@"count"] integerValue];
    XCTAssertEqual(count, 1, @"expected one record for dictionary %@", dictionary);
    return 0;
}];

占位符 ? 的使用

FMDB中也支持占位符的使用:

INSERT INTO myTable VALUES (?, ?, ?, ?)

举例:

BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", @(identifier), name, date, comment ?: [NSNull null]];
if (!success) {
    NSLog(@"error = %@", [db lastErrorMessage]);
}

占位符的对象,应该是NSObject对象。 SQL语句中的NULL ,用[NSNull null]来替代。

named parameters syntax

NSDictionary *arguments = @{@"identifier": @(identifier), @"name": name, @"date": date, @"comment": comment ?: [NSNull null]};
BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment)" withParameterDictionary:arguments];
if (!success) {
    NSLog(@"error = %@", [db lastErrorMessage]);
}

使用参数Dictionary来传递参数。

关于线程安全

多线程中使用一个FMDatabase实例,是不行的。一般一个线程使用一个FMDatabase对象。而在多个线程中共用一个FMDatabaseQueue是安全的。

使用方式 :

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
[queue inDatabase:^(FMDatabase *db) {
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3];

    FMResultSet *rs = [db executeQuery:@"select * from foo"];
    while ([rs next]) {
        …
    }
}];

FMDatabaseQueue将SQL语句都放在了一个GCD队列中运行,所以是线程安全的。