免费开源的iOS开发学习平台

FMDB:2-更新操作与查询操作

在FMDB中,把对数据库的操作合并为两类,第一类是查询操作,即使用SELECT语句执行的操作,查询操作的特点是会返回查询结果,继而对查询结果做进一步的处理;第二类是更新操作,凡是涉及到更新数据库存储内容的操作都属于更新操作,例如在SQL中的UPDATE语句、INSERT语句等。

更新操作

在SQL语句中,所有非SELECT语句的都可以看成是更新操作,包括CREATE、UPDATE、INSERT、ALTER、DELETE、DROP等语句。在FMDB中,提供了executeUpdate:方法用于执行更新操作,需要执行的SQL语句作为参数传入该方法中。

- (BOOL)executeUpdate:(NSString*)sql, ...;

执行更新语句会返回一个BOOL值。如果是YES则表示更新成功,如果是NO则表示更新失败。可以调用lastErrorMessage和lastErrorCode方法来获取失败的原因。

- (NSString*)lastErrorMessage;
- (int)lastErrorCode;

查询操作

在FMDB中,执行的SELECT语句对应查询操作。所有的查询操作可以通过executeQuery:方法来执行,并且查询结果会作为该方法的返回值。

- (FMResultSet *)executeQuery:(NSString*)sql, ...;

执行查询语句之后,如果成功会返回一个FMResultSet对象。如果失败会返回nil,我们同样可以调用lastErrorMessage和lastErrorCode方法来获取失败的原因。

为了遍历查询的结果集,需要使用while()循环。并且调用FMResultSet类的next方法,从一条记录移动到下一条记录。例如:

FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
    //可以获取到每条查询记录的值
}

FMResultSet有很多方法来获取对应的值,主要包括两类,第一类是通过列名获取对应的值,第二类是通过列序号获取对应的值。例如,FMResultSet.h中提供了如下根据列名获取数值的方法:

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

同样也有{type}ForColumnIndex:方法来根据列序号获取不同的值。这些方法能通过位置,而不是通过列名来获取相对应的值。

通常来说我们不用手动调用FMResultSet的close方法。FMResultSet对象销毁或者数据库关闭时会自动调用FMResultSet的-close方法。

执行多条语句

当我们需要执行多条SQL语句时,FMDB也为我们提供了快捷方法,并不需要一条一条的执行。可以通过FMDatabase类中的executeStatements:方法来执行多条语句。

- (BOOL)executeStatements:(NSString *)sql;
- (BOOL)executeStatements:(NSString *)sql withResultBlock:(FMDBExecuteStatementsCallbackBlock)block;

下方的示例代码中,我们把多条SQL语句都封装在一个NSString类型的对象中执行。

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;
}];

SQL语句格式处理

在编写SQL语句时不必手动去拼接,可以使用标准的SQLite绑定语法:

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

这样拼接SQL语句会更安全。?表示一个占位符,会被后续传进来的值所替代。所有的执行方法都接受一组参数(比如NSArray,NSDictionary或者va_list),然后会做一些适当的转义。

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需要变成NSNumber, NULL需要变成[NSNULL null]。

示例代码

https://github.com/99ios/13.6