项目中使用 ARC 还是 MRC,对使用 FMDB 都没有任何影响,FMDB 会在编译项目时自动匹配。
一、类使用
- FMDatabase: 代表单个数据库,用它来执行SQL语句。
- FMResultSet: 代表数据库的查询。
- FMDatabaseQueue:如果数据库在多线程要进行查询和更新,就使用FMDatabaseQueue这个类,
二、数据库的创建
FMDatabase类可以通过数据库路径来创建数据库,如下有三种方式得到数据路径
1、系统路径: 在磁盘上没有文件,如果上磁盘上没有数据库文件,FMDatabase会为我们创建。
2、空的字符串:如果数据库的路径是空字符串时,数据库将放在沙盒中temporary,如果数据库没有连接时,数据库将会被删除。
3、NULL的数据库路径:如果数据库路径是NULL,数据库会被创建,但是数据库没有连接时,数据库将会被删除。
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.db"];
FMDatabase *db = [FMDatabase databaseWithPath:path];
三、打开数据库
在使用数据库之前,一定要打开数据库,果没有足够的资源和权限来打开\创建数据库,数据库会打开失败。
//当数据打开失败时,作为处理
if (![db open])
{
db = nil;
return;
}
三、数据库更新
除了SELECT语句外,所有的SQL语句都是更新,更新包括CREATE, UPDATE, INSERT, ALTER, COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE 声明(甚至更多)。如果SQL语句没有SELECT ,那么该SQL语句是更新。执行更新SQL语句时,会返回单个数值如BOOL。
执行更新语句后会返回一个 BOOL 值,返回 YES 表示执行更新语句成功,返回 NO 表示出现错误,可以通过调用 -lastErrorMessage 和 -lastErrorCode 方法获取更多错误信息。
关于SQL语句的使用,一、数据库之SQL语句;
四、数据库查询
用-executeQuery...方法来查询数据库内容。如果查询数据成功会返回FMResultSet,否则返回是一个空。-lastErrorMessage 和 -lastErrorCode方法查找失败原因。
为了得到数据库查询的结果,使用while循环,在FMDB中可以轻松的实现。
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
//retrieve values for each record
}
即使只需要获取一个数据,也还是必须在访问查询结果前调用 -[FMResultSet next]。
FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
if ([s next]) {
int totalCount = [s intForColumnIndex:0];
}
FMResultSet有如下方法得到每列中的相应的数据
- intForColumn:
- longForColumn:
- longLongIntForColumn:
- boolForColumn:
- doubleForColumn:
- stringForColumn:
- dateForColumn:
- dataForColumn:
- dataNoCopyForColumn:
- UTF8StringForColumnName:
- objectForColumnName:
这些方法都有一个 {type}ForColumnIndex: 变体,是基于列的位置来查询数据。
通常情况下,一个 FMResultSet 没有必要手动 -close,因为结果集合 (result set) 被释放或者源数据库关闭会自动关闭。
五、关闭数据库
当完成数据库的查询和更新时,需要调用 -close方法去关闭数据库。
[db close];
六、事务
通过合理的方法或者执行 begin/end transaction语句,FMDatabase可以开始和提交事务。
七、数据库批量语句和操作
可以用FMDatabase 的executeStatements:withResul-tBlock: 方法中执行多个字符串语句。
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提供SQL语句时,执行语句前不要尝试处理任何值,应该参照如下的写法。
INSERT INTO myTable VALUES (?, ?, ?, ?)
?可以被SQLite识别,并将它作为一个占位符。FMDB 执行语句的方法都接受多个参数 (比如 NSArray,NSDict-ionary,va_list),并将它们转义成正确数值。
在Objcetive-C中SQL用?作为占位符。
NSInteger identifier = 42;
NSString *name = @"Liam O'Flaherty (\"the famous Irish author\")";
NSDate *date = [NSDate date];
NSString *comment = nil;
BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", @(identifier), name, date, comment ?: [NSNull null]];
if (!success) {
NSLog(@"error = %@", [db lastErrorMessage]);
}
Fundamental 类型的数据,如identifier是整形,整形应该将转成NSNumber,如上面代码用@将整形转化成NSN-umber。或者用[NSNumber numberWithInt:identi-fier]方法将整形转化成NSNumber。
同样,SQL NULL 值应该是插入[NSNull null],如上面代码中comment可能为空,使用comment ?: [NSNull null]语句,如果comment 不为空时,将插入字符串,否则则插入[NSNull null];
或者,可以用使用如下语句
INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment)
参数一定要是有冒号。SQLite本身支持其他的符号,在字典的键前面有冒号,一定不能在字典前面用冒号
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]);
}
最重要一点是,SQL语句中不要用NSString方法给拼接字符串,如stringWithFormat。
九、FMDatabaseQueue 和线程安全
FMDatabase 这个类是线程不安全的,在多线程中使用 FMDatabase 单例是极其错误做法。不能在多线程的环境中对数据库 FMDatabase 进行读写,会出现奔溃或者异常,因为你不能保证你读数据的同时另外一条线程不在写数据。因此不能在多线程中使用FMDatabase 单例。
相反使用在多线程中作用FMDatabaseQueue。用FMDatabaseQueue单例和在多个线程中使用它。FMDatabaseQueue单例是跨线程是同步的。如下是如何使用它。
//创建自己的队列
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
//使用FMDatabaseQueue
[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在事务中的使用
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3];
if (whoopsSomethingWrongHappened) {
*rollback = YES;
return;
}
// etc ...
}];
如上是翻译自官方文档,和参考FMDB 使用方法,其中Swift部分略去,请见谅;
由于时间有限,不足之处,请指正,感谢!
网友评论