FMDB 使用方法
优秀的第三方库,README 也是很优秀的,理解了 README,会对使用带来很多便利。
ARC 和 MRC
项目中使用 ARC 还是 MRC,对使用 FMDB 都没有任何影响,FMDB 会在编译项目时自动匹配。
使用
在 FMDB 中有三个重要的类:
FMDatabase
:是一个提供 SQLite 数据库的类,用于执行 SQL 语句。FMResultSet
:用在FMDatabase
中执行查询的结果的类。FMDatabaseQueue
:在多线程下查询和更新数据库用到的类。
数据库创建
FMDatabase
是通过一个 SQLite 数据库文件路径创建的,此路径可以是以下三者之一:
- 一个文件的系统路径。磁盘中可以不存在此文件,因为如果不存在会自动为你创建。
- 一个空的字符串
@""
。会在临时位置创建一个空的数据库,当FMDatabase
连接关闭时,该数据库会被删除。 NULL
。会在内存中创建一个数据库,当FMDatabase
连接关闭时,该数据库会被销毁。
// 创建数据库示例
FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
打开数据库
数据库必须是打开状态,才能与之交互。如果没有足够的资源和权限来打开\创建数据库,数据库会打开失败。
数据库更新
SQL 语句中除过 SELECT
语句都可以称之为更新操作。包括 CREATE
,UPDATE
,INSERT
,ALTER
,COMMIT
,BEGIN
,DETACH
,DROP
,END
,EXPLAIN
,VACUUM
,REPLACE
等。一般只要不是以 SELECT
开头的 SQL 语句,都是更新语句。
执行更新语句后会返回一个 BOOL
值,返回 YES
表示执行更新语句成功,返回 NO
表示出现错误,可以通过调用 -lastErrorMessage
和 -lastErrorCode
方法获取更多错误信息。
数据库查询
通过调用 -executeQuery...
方法之一执行 SELECT
语句进行数据库查询操作。
执行查询操作后,如果成功会返回一个 FMResultSet
对象,反之会返回 nil
。通过 -lastErrorMessage
和 -lastErrorCode
方法可以确定为什么会查询失败。
为了遍历查询结果,需要 while()
循环,然后逐条记录查看。在 FMDB 中,可以通过下面的简单方式实现:
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
// 每条记录的检索值
}
即使只需要获取一个数据,也还是必须在访问查询结果前调用 -[FMResultSet next]
。
// 示例
FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
if ([s next]) {
int totalCount = [s intForColumnIndex:0];
}
FMResultSet
提供了很多方便的方法来查询数据:
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dateForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumn:
objectForColumn:
这些方法都有一个 {type}ForColumnIndex:
变体,是基于列的位置来查询数据。
通常情况下,一个 FMResultSet
没有必要手动 -close
,因为结果集合 (result set) 被释放或者源数据库关闭会自动关闭。
关闭数据库
当对数据库进行查询和更新操作完成后,需要调用 -close
关闭数据库 FMDatabase
的连接。
// 示例
[db close];
事务
FMDatabase
可以通过调用方法来开始和提交事务,也可以通过执行开始\结束事务 (begin\end transaction) 语句。
多语句和批处理
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;
}];
数据处理
当给 FMDB 提供 SQL 语句时,在插入前不应该处理任何数据,而应该使用标准的 SQLite 的绑定语法。
// 示例
INSERT INTO myTable VALUES (?, ?, ?)
?
问号在 SQLite 中意为即将插入的值的占位符,FMDB 执行语句的方法都接受多个参数 (或者参数集合,比如 NSArray
,NSDictionary
,va_list
),它们都会正确转义。
也可以使用命名参数语法:
// 示例
INSERT INTO myTable VALUES (:id, :name, :value)
这些参数必须以冒号开头,SQLite 自身支持其他字符,但是命名时字典的键内部以冒号开头,就不能在你的字典的键中包含冒号。
// 示例
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];
-execute*WithFormat:
这些方法后面都可以接格式字符串参数,以下 % 百分号格式符都是可以识别的:%@
, %c
, %s
, %d
, %D
, %i
, %u
, %U
, %hi
, %hu
, %qi
, %qu
, %f
, %g
, %ld
, %lu
, %lld
, %llu
。使用其他格式符可能会出现不可预知的问题。出于某种原因,可能需要在你的 SQL 语句中使用 %
字符,应该使用百分号转义一下 %%
。
FMDatabaseQueue 队列和线程安全
在多线程中同时使用 FMDatabase 单例是极其错误的想法,会导致每个线程创建一个 FMDatabase 对象。不要跨线程使用单例,也不要同时跨多线程,不然会奔溃或者异常。
因此不要实例化一个 FMDatabase 单例来跨线程使用。
相反,使用 FMDatabaseQueue,下面就是它的使用方法:
第一,创建队列。
// 创建 FMdatabaseQueue 示例
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;
}
// ...
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
}];
FMDatabase 将块代码 block 运行在一个串行队列上,即使在多线程同时调用 FMDatabaseQueue 的方法,它们仍然还是顺序执行。这种查询和更新方式不会影响其它,是线程安全的。
基于 block 自定义 SQLite 函数
这是可以的,例子可以在 main.m 中的 makeFunctionNamed:
方法查看。
Swift
在 Swift 项目中也可以使用 FMDB,需要做以下步骤:
- 将 FMDB 的
.m
和.h
全部文件拖进你的项目。 - 如果 Xcode 提示创建桥接文件,需要点击创建。如果没有提示,且项目中也没有桥接文件,需要手动添加。点此查看桥接文件更多信息
- 在桥接文件中,添加这行代码:
#import "FMDB.h"
- 可以从 "src/extra/Swift Extension" 文件夹中拷贝
FMDatabaseVariadic.swift
文件到项目中,就可以使用executeUpdate
和executeQuery
多参数了。
做完上述几步,就可以使用 FMDatabase 写 Swift 代码了。
// 示例
let documentsFolder = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let path = documentsFolder.stringByAppendingPathComponent("test.sqlite")
let database = FMDatabase(path: path)
if !database.open() {
println("Unable to open database")
return
}
if !database.executeUpdate("create table test(x text, y text, z text)", withArgumentsInArray: nil) {
println("create table failed: \(database.lastErrorMessage())")
}
if !database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["a", "b", "c"]) {
println("insert 1 table failed: \(database.lastErrorMessage())")
}
if !database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["e", "f", "g"]) {
println("insert 2 table failed: \(database.lastErrorMessage())")
}
if let rs = database.executeQuery("select x, y, z from test", withArgumentsInArray: nil) {
while rs.next() {
let x = rs.stringForColumn("x")
let y = rs.stringForColumn("y")
let z = rs.stringForColumn("z")
println("x = \(x); y = \(y); z = \(z)")
}
} else {
println("select failed: \(database.lastErrorMessage())")
}
database.close()
本文实际是对 FMDB 的 README 简单翻译,以方便使用 FMDB。
相关链接:
FMDB 使用方法的更多相关文章
- CoreData和FMDB你用哪个?
概括: 我们先说说这两个东西,CoreData 和 FMDB,其实就我自己而言觉得这两个都不错,刚开始是接触FMDB的,CoreData是工作后自己看的.苹果推荐开发者去使用CoreData,但 FM ...
- 如何写一个FMDB帮助类?看看runtime吧
FMDB是一个封装很好的sqllite类库.项目中调用的时候只需要写SQL语句,就能实现数据的CURD.我试过即使手写SQL语句也很麻烦,需要一个字段一个字段的拼上去,而且容易出错.有没有动态获取字段 ...
- iOS——使用FMDB进行数据库操作(转载)
iOS 使用FMDB进行数据库操作 https://github.com/ccgus/fmdb [摘要]本文介绍iOS 使用FMDB进行数据库操作,并提供详细的示例代码供参考. FMDB 使用方法 A ...
- cocospods 最新安装教程
Terminator 终端原来 安装 cocoa pods 终端命令 :sudo gem install cocoapods #已经无效系统更新后的 cocoa pods 终端命令 : sudo ...
- 数据持久化(五)之CoreData
@简单的说,Core Data就是能够存储到磁盘的对象图,[...]Core Data能够帮我们做非常多任务作.它能够作为软件的整个模型层. 它不只在磁盘上存储数据.也把我们须要的数据对象读取到内存中 ...
- javaSE27天复习总结
JAVA学习总结 2 第一天 2 1:计算机概述(了解) 2 (1)计算机 2 (2)计算机硬件 2 (3)计算机软件 2 (4)软件开发(理解) 2 (5) ...
- FMDB的使用方法
转自:http://blog.devtang.com/blog/2012/04/22/use-fmdb/ 前言 SQLite (http://www.sqlite.org/docs.html) 是一个 ...
- Swift基础之对FMDB第三方的使用方法
相信大家都熟悉OC使用FMDB第三方库,进行数据库操作,增.删.改.查,现在我就来利用代码展示一下Swift对此库的使用方法,我是通过Pods添加的第三方库,如果手动添加记得创建桥接文件,在文件中调用 ...
- FMDB的使用方法(附Demo)
http://www.jianshu.com/p/54e74ce87404 最近在项目中需要在多个页面对同样的数据进行相关操作,于是便用到了FMDB数据库操作,以下便是FMDB的一些简单的使用方法.附 ...
随机推荐
- 破解 “PEDIY CrackMe 2007” 之 KeygenMe_1_by_boonz
系统 : Windows xp 程序 :KeygenMe_1_by_boonz 程序下载地址 :http://www.crackmes.de/users/boonz/keygenme_1_by_boo ...
- Android-adb 常用命令 和 sqlite
Android开发环境中,ADB是我们进行Android开发经常要用的调试工具,它的使用当然是我们Android开发者必须要掌握的. ADB概述 Android Debug Bridge,Androi ...
- Quartz 2d绘图
今天看了一下Quartz 2D绘图,我只想说:不要把绘图和动画那些东西当做一个很复杂的东西,其实只要你认真看还是可以理解的.他们并不难.啰嗦了几句,现在直接进入正题: 前提是我们必须新建一个singl ...
- Objective-C的IO流
- ssh key scp命令 scp无密码传输
ssh ~/.ssh/目录下通常有个文件 [root@user .ssh]# ll 总用量 16 -rw-------. 1 root root 552 11月 16 02:48 authorized ...
- SAM4E单片机之旅——23、在AS6(GCC)中使用FPU
浮点单元(Floating Point Unit,FPU),是用于处理浮点数运算的单元. 为使用FPU,除了需要启用FPU外,还需要对编译器进行设置,以使其针对浮点运算生成特殊的指令.虽然在Atmel ...
- Effective Java 73 Avoid thread groups
Thread groups were originally envisioned as a mechanism for isolating applets for security purposes. ...
- Servlet生命周期+工作原理
Servlet生命周期+工作原理 1.Servlet的生命周期: Servlet加载,加载,服务,销毁. 2.典型函数解释: Init():这个函数是用来初始化Servlet对象的.在 ...
- IE6-9中tbody的innerHTML不能赋值bug
IE6-IE9中tbody的innerHTML不能赋值,重现代码如下 <!DOCTYPE html> <html> <head> <meta charset= ...
- Mac SVN ignore 等相关
OSX自带了SVN命令行,通过终端就可以使用了. 一.SVN ignore Mac的SVN想把node_modules 忽略,即svn status时(svn st缩写)不显示node_nodules ...