MENU

FMDB 的简单使用

May 1, 2016 • Read: 553 • Codes

FMDB 里有三个主要的类:

  • FMDatabase:相当于一个单独的 SQLite 数据库,用来执行 SQL 语句。
  • FMResultSet:使用 FMDatabase 执行查询语句后返回的结果集。
  • FMDatabaseQueue:可以用来优化 SQL 查询,或在多线程环境中执行更新语句,线程安全。但这里就不讲它了(为什么?就因为今天是五月一号🤣🤣🤣)

创建数据库

两种方法:

FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
FMDatabase *db = [[FMDatabase alloc] initWithPath:@"/tmp/tmp.db"];

两种方法是一样的,提供一个参数pathpath需要是一个文件系统路径,如果文件存在,则使用此数据库文件,如果不存在,则新建一个空的数据库文件。

另外,如果path为一个空字符串(@""),则会在缓存目录中新建一个空的数据库数据库文件,关闭后删除。如果pathNULL,则会创建一个内存数据库,同样的,关闭后会被销毁。

打开数据库

创建完数据库后需要打开数据库,才能开始执行 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]);
}
Archives QR Code Tip
QR Code for this page
Tipping QR Code
Leave a Comment