FMDB中主要有三个类:
FMDatabase
- 代表单独的一个SQLite数据库。用来执行SQL语句。FMResultSet
- 代表FMDatabase
查询的结果。FMDatabaseQueue
- 如果你想在多线程中执行查询和更新,你就会使用到这个类。要使用一个路径来创建一个FMDatabase
。路径有如下三种方式:
@""
)。在一个临时的位置创建一个空的数据库。当FMDatabase
的连接被关闭的时候,数据库会被销毁。NULL
。一个in-memory的数据库会被创建。当FMDatabase
的连接被关闭的时候,数据库会被销毁。FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
在和数据库交互前,必须打开数据库。可能会打开失败:
if (![db open]) {
[db release];
return;
}
除了SELECT
语句,其它的SQL语句都被认为是更新(update)。包括CREATE
, UPDATE
, INSERT
, ALTER
, COMMIT
, BEGIN
, DETACH
, DELETE
, DROP
, END
, EXPLAIN
, VACUUM
, 和 REPLACE
。基本上,如果你的SQL语句不是以SELECT
开头,就都是更新语句。
执行更新,返回一个BOOL
值。返回YES
表示更新成功,返回NO
表示有错误发生。你可以调用-lastErrorMessage
和-lastErrorCode
方法来获取更多的信息。
通过-executeQuery...
其中之一的方法来执行一个SELECT
,SELECT
一个查询。
执行查询成功,返回的是一个FMResultSet
对象,失败就返回nil
。
使用while()
循环来遍历结果。
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
有许多方法来获取特定格式的数据:
Typically, there’s no need to -close
an FMResultSet
yourself, since that happens when either the result set is deallocated, or the parent database is closed.
当完成数据库的查询和更新之后,你应该-close
数据库FMDatabase
的连接。
[db close];
FMDatabase
can begin and commit a transaction by invoking one of the appropriate methods or executing a begin/end transaction statement.
使用FMDatabase
的executeStatements:withResultBlock:
方法,来执行多个语句:
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;
}];
要使用标准的SQLite绑定语法:
INSERT INTO myTable VALUES (?, ?, ?)
?
表示被插入值得占位符。
相应的,你也可以使用named parameters syntax:
INSERT INTO myTable VALUES (:id, :name, :value)
参数必须以冒号开头。SQLite支持其它的字符,但是字典的keys带有一个冒号的前缀,所有在字典的keys中不要包含冒号:
NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:@"My Name", @"name", nil];
[db executeUpdate:@"INSERT INTO myTable (name) VALUES (:name)" withParameterDictionary:argsDict];
另外,请不要这样做:
[db executeUpdate:[NSString stringWithFormat:@"INSERT INTO myTable VALUES (%@)", @"this has \" lots of ' bizarre \" quotes '"]];
相反,应该这么做:
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @"this has \" lots of ' bizarre \" quotes '"];
提供给-executeUpdate:
方法的参数都必须是对象。下面的就是错的,会导致崩溃:
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];
插入一个数字的正确方法是使用NSNumber
对象:
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];
另一种方法是,使用-execute*WithFormat:
,类似于字符串替换:
[db executeUpdateWithFormat:@"INSERT INTO myTable VALUES (%d)", 42];
在多个线程中使用一个FMDatabase的实例是个坏主意。
So don’t instantiate a single FMDatabase object and use it across multiple threads.
要使用FMDatabaseQueue
。下面是使用的方法:
首先,创建queue
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]) { … } }];
把things包裹在一个事务里,可以这样做:
[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]]; }];