唐巧的博客

在iOS开发中使用FMDB

字数统计: 1.4k阅读时长: 5 min
2012/04/22

前言

SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库。iOS SDK 很早就支持了 SQLite,在使用时,只需要加入 libsqlite3.dylib 依赖以及引入 sqlite3.h 头文件即可。但是,原生的 SQLite API 在使用上相当不友好,在使用时,非常不便。于是,开源社区中就出现了一系列将 SQLite API 进行封装的库,而 FMDB (https://github.com/ccgus/fmdb) 则是开源社区中的优秀者。

FMDB 在使用上相当方便。以下是一个简单的例子:

NSString* docsdir = [NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString* dbpath = [docsdir stringByAppendingPathComponent:@"user.sqlite"];
FMDatabase* db = [FMDatabase databaseWithPath:dbpath];
[db open];
FMResultSet *rs = [db executeQuery:@"select * from people"];
while ([rs next]) {
NSLog(@"%@ %@",
[rs stringForColumn:@"firstname"],
[rs stringForColumn:@"lastname"]);
}
[db close];

可以看到,使用 FMDB 后的数据库代码清晰明了,比原生的 API 优雅多了。另外,FMDB 同时兼容 ARC 和非 ARC 工程,会自动根据工程配置来调整相关的内存管理代码。

使用说明

该使用说明主要翻译自 fmdb 的 github 项目说明文档: https://github.com/ccgus/fmdb

引入相关文件

首先将 FMDB 从 github 上 clone 下来,然后将以下文件 copy 到你的工程中:

FMDatabase.h
FMDatabase.m
FMDatabaseAdditions.h
FMDatabaseAdditions.m
FMDatabasePool.h
FMDatabasePool.m
FMDatabaseQueue.h
FMDatabaseQueue.m
FMResultSet.h
FMResultSet.m

建立数据库

建立数据库只需要如下一行即可 , 当该文件不存在时,fmdb 会自己创建一个。如果你传入的参数是空串:@”” ,则 fmdb 会在临时文件目录下创建这个数据库,如果你传入的参数是 NULL,则它会建立一个在内存中的数据库。

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

打开数据库

使用如下语句,如果打开失败,可能是权限不足或者资源不足。通常打开完操作操作后,需要调用 close 方法来关闭数据库。

if (![db open]) {
// error
return;
}
// some operation
// ...

[db close];

执行更新操作

除了 Select 操作之外,其它的都是更新操作。更新操作使用如下方法,如果有错误,可以用 error 参数中获得。

-[FMDatabase executeUpdate:error:withArgumentsInArray:orVAList:]

执行查询操作

查询操作示例如下。注意:即使操作结果只有一行,也需要先调用 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];
}

FMDB 提供如下多个方法来获取不同类型的数据:

intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dateForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnIndex:
objectForColumn:

通常情况下,你并不需要关闭 FMResultSet,因为相关的数据库关闭时,FMResultSet 也会被自动关闭。

数据参数

通常情况下,你可以按照标准的 SQL 语句,用 ? 表示执行语句的参数,如:

INSERT INTO myTable VALUES (?, ?, ?)

然后,可以我们可以调用 executeUpdate 方法来将 ? 所指代的具体参数传入,通常是用变长参数来传递进去的,如下:

NSString *sql = @"insert into User (name, password) values (?, ?)";
[db executeUpdate:sql, user.name, user.password];

这里需要注意的是,参数必须是 NSObject 的子类,所以象 int,double,bool 这种基本类型,需要封装成对应的包装类才行,如下所示:

// 错误,42 不能作为参数
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];
// 正确,将 42 封装成 NSNumber 类
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];

线程安全

如果我们的 app 需要多线程操作数据库,那么就需要使用 FMDatabaseQueue 来保证线程安全了。
切记不能在多个线程中共同一个 FMDatabase 对象并且在多个线程中同时使用,这个类本身不是线程安全的,这样使用会造成数据混乱等问题。

使用 FMDatabaseQueue 很简单,首先用一个数据库文件地址来初使化 FMDatabaseQueue,然后就可以将一个闭包 (block) 传入 inDatabase 方法中。
在闭包中操作数据库,而不直接参与 FMDatabase 的管理。

// 创建,最好放在一个单例的类中
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]) {
// …
}
}];

// 如果要支持事务
[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]];
}];

工具

为了查看 Sqlite 中的数据,一个好的图形化界面的数据库管理程序是必不可少的。mysql 有 phpMyAdmin,那么 sqlite 呢?

我主要使用的是 Firefox 的一个名为 SQLite Manager 的插件,安装此插件后,可以直接打开后缀名为 sqlite 的数据库文件。SQLite Manager 提供一个图形化的界面来执行数据查询或更改操作。如下图所示:

总结

FMDB 将 SQLite API 进行了很友好的封装,使用上非常方便,对于那些使用纯 Sqlite API 来进行数据库操作的 app,可以考虑将其迁移到基于 FMDB 上,这对于以后数据库相关功能的开发维护,可以提高不少效率。

我在学习 fmdb 的时候做了一个小工程用于练习,我把它放到 github 上了。感兴趣的可以自行下载:https://github.com/tangqiaoboy/FmdbSample

祝大家玩得开心。

CATALOG
  1. 1. 前言
  2. 2. 使用说明
    1. 2.1. 引入相关文件
    2. 2.2. 建立数据库
    3. 2.3. 打开数据库
    4. 2.4. 执行更新操作
    5. 2.5. 执行查询操作
    6. 2.6. 数据参数
    7. 2.7. 线程安全
  3. 3. 工具
  4. 4. 总结