FMDB 里有三个主要的类:
FMDatabase:相当于一个单独的 SQLite 数据库,用来执行 SQL 语句。FMResultSet:使用FMDatabase执行查询语句后返回的结果集。FMDatabaseQueue:可以用来优化 SQL 查询,或在多线程环境中执行更新语句,线程安全。但这里就不讲它了(为什么?就因为今天是五月一号🤣🤣🤣)
创建数据库
两种方法:
FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];FMDatabase *db = [[FMDatabase alloc] initWithPath:@"/tmp/tmp.db"];两种方法是一样的,提供一个参数path,path需要是一个文件系统路径,如果文件存在,则使用此数据库文件,如果不存在,则新建一个空的数据库文件。
另外,如果path为一个空字符串(@""),则会在缓存目录中新建一个空的数据库数据库文件,关闭后删除。如果path为NULL,则会创建一个内存数据库,同样的,关闭后会被销毁。
打开数据库
创建完数据库后需要打开数据库,才能开始执行 SQL 语句。
if (![db open]) {
return;
}关闭数据库
在我们使用完数据库后,我们需要关闭数据库以释放资源
[db close];执行查询
执行查询使用- executeQuery...方法,查询成功会返回一个FMResultSet对象,否则返回nil。
在从结果集中获取结果时,必须先调用- [FMResultSet next]方法:
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
//retrieve values for each record
}即使只想获取第一个查询结果时也是一样:
FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
if ([s next]) {
int totalCount = [s intForColumnIndex:0];
}从结果集中获取查询结果
获取查询结果时,可以根据列名使用- [FMResultSet *ForColumn:],如果你想通过index获取,可以使用- [FMResultSet *ForColumnIndex:]。
可用的方法如下:
intForColumn:/intForColumnIndex:longForColumn:/longForColumnIndex:longLongIntForColumn:/longLongIntForColumnIndex:boolForColumn:/boolForColumnIndex:doubleForColumn:/doubleForColumnIndex:stringForColumn:/stringForColumnIndex:dateForColumn:/dateForColumnIndex:dataForColumn:/dataForColumnIndex:dataNoCopyForColumn:/dataNoCopyForColumnIndex:UTF8StringForColumn:/UTF8StringForColumnIndex:objectForColumn:/objectForColumnIndex:
通常情况下,我们不需要主动调用结果集的- close方法,- close方法会在结果集被释放或数据库被关闭时自动调用。
执行更新
在 SQL 语句中,除了SELECT语句,其他的都可以称之为更新语句。更新语句可以使用- executeUpdate...方法。
更新方法会返回一个BOOL型结果,告诉我们更新是否执行成功了。如果失败了,我们可以调用- lastErrorMessage获取失败原因。
BOOL success = [db executeUpdate:@"INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", @(identifier), name, date, comment ?: [NSNull null]];
if (!success) {
NSLog(@"error = %@", [db lastErrorMessage]);
}另外,如果我们想获取刚刚插入的数据的行号,可以使用- [FMDatabse lastInsertRowId]方法获取。
数据绑定
为了防止 SQL 注入攻击,我们在拼接 SQL 时,应该避免将变量,特别是用户输入的信息直接拼在 SQL 中,而是应该使用数据绑定的方式。
例如:
有如下查询语句:
SELECT * FROM myTable WHERE name = 'Ann';如果我们自己拼接,可能会使用下面这种方式:
[NSString stringWithFormat:@"SELECT * FROM myTable WHERE name = '%@';", name];那么,如果用户输入的 name 为Ann' OR '1' = '1,那么 SQL 就变成了:
SELECT * FROM myTable WHERE name = 'Ann' OR '1' = '1';于是,整张myTable表的信息便都会被查询出来,甚至,如果用户输入了:
Ann'; DELETE FROM myTable WHERE '1' = '1于是,SQL 变成了:
SELECT * FROM myTable WHERE name = 'Ann'; DELETE FROM myTable WHERE '1' = '1';你懂得。
数据绑定常使用?作为占位符,也可是给占位符命名,如:name,示例如下:
INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)示例:
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]);
}值得注意的是:我们需要将非引用类型(如:NSInteger、int、long等等)转换为引用类型。可以简单的使用@(anIntegerValue)或使用NSNumber的相关方法或其他方法。
对于给占位符命名的方式:
INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment)示例:
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]);
}