在iOS 开发中,总会用到数据库的操作,来实现增、删、改、查。 尤其是查询。 一旦涉及到查询的操作,那就非得用数据库不可了。
 
SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库。 iOS SDK 从诞生之日起就支持SQLite。 在使用时,只需要加入  libsqlite3.dylib ,并引入 sqlite3.h 头文件即可。 但是,原生的SQLite API 在使用上 非常不便,于是,开源社区就出现了一系列的 将SQLite API 进行封装的库,而FMDB  (https://github.com/ccgus/fmdb) 则是开源社区中的佼佼者。

实现步骤:

1. 将FMDB 从 https://github.com/ccgus/fmdb 下载下来。

 
Usage

There are three main classes in FMDB:

  1. FMDatabase - Represents a single SQLite database. Used for executing SQL statements.
  2. FMResultSet - Represents the results of executing a query on an FMDatabase.
  3. FMDatabaseQueue - If you're wanting to perform queries and updates on multiple threads, you'll want to use this class. It's described in the "Thread Safety" section below.

Database Creation

An FMDatabase is created with a path to a SQLite database file. This path can be one of these three:

  1. A file system path. The file does not have to exist on disk. If it does not exist, it is created for you.
  2. An empty string (@""). An empty database is created at a temporary location. This database is deleted with the FMDatabase connection is closed.
  3. NULL. An in-memory database is created. This database will be destroyed with the FMDatabaseconnection is closed.

(For more information on temporary and in-memory databases, read the sqlite documentation on the subject: http://www.sqlite.org/inmemorydb.html)

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

Opening

Before you can interact with the database, it must be opened. Opening fails if there are insufficient resources or permissions to open and/or create the database.

if (![db open]) {
[db release];
return;
}

Executing Updates

Any sort of SQL statement which is not a SELECT statement qualifies as an update. This includesCREATEUPDATEINSERTALTERCOMMITBEGINDETACHDELETEDROPENDEXPLAINVACUUM, and REPLACE statements (plus many more). Basically, if your SQL statement does not begin withSELECT, it is an update statement.

Executing updates returns a single value, a BOOL. A return value of YES means the update was successfully executed, and a return value of NO means that some error was encountered. You may invoke the -lastErrorMessage and -lastErrorCode methods to retrieve more information.

Executing Queries

SELECT statement is a query and is executed via one of the -executeQuery... methods.

Executing queries returns an FMResultSet object if successful, and nil upon failure. Like executing updates, there is a variant that accepts an NSError ** parameter. Otherwise you should use the -lastErrorMessage and -lastErrorCode methods to determine why a query failed.

In order to iterate through the results of your query, you use a while() loop. You also need to "step" from one record to the other. With FMDB, the easiest way to do that is like this:

FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
//retrieve values for each record
}

You must always invoke -[FMResultSet next] before attempting to access the values returned in a query, even if you're only expecting one:

FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
if ([s next]) {
int totalCount = [s intForColumnIndex:0];
}

FMResultSet has many methods to retrieve data in an appropriate format:

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

Each of these methods also has a {type}ForColumnIndex: variant that is used to retrieve the data based on the position of the column in the results, as opposed to the column's name.

Typically, there's no need to -close an FMResultSet yourself, since that happens when either the result set is deallocated, or the parent database is closed.

Closing

When you have finished executing queries and updates on the database, you should -close theFMDatabase connection so that SQLite will relinquish any resources it has acquired during the course of its operation.

[db close];

Transactions

FMDatabase can begin and commit a transaction by invoking one of the appropriate methods or executing a begin/end transaction statement.

Multiple Statements and Batch Stuff

You can use FMDatabase's executeStatements:withResultBlock: to do multiple statements in a string:

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

Data Sanitization

When providing a SQL statement to FMDB, you should not attempt to "sanitize" any values before insertion. Instead, you should use the standard SQLite binding syntax:

INSERT INTO myTable VALUES (?, ?, ?)

The ? character is recognized by SQLite as a placeholder for a value to be inserted. The execution methods all accept a variable number of arguments (or a representation of those arguments, such as an NSArrayNSDictionary, or a va_list), which are properly escaped for you.

Alternatively, you may use named parameters syntax:

INSERT INTO myTable VALUES (:id, :name, :value)

The parameters must start with a colon. SQLite itself supports other characters, but internally the Dictionary keys are prefixed with a colon, do not include the colon in your dictionary keys.

NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:@"My Name", @"name", nil];
[db executeUpdate:@"INSERT INTO myTable (name) VALUES (:name)" withParameterDictionary:argsDict];

Thus, you SHOULD NOT do this (or anything like this):

[db executeUpdate:[NSString stringWithFormat:@"INSERT INTO myTable VALUES (%@)", @"this has \" lots of ' bizarre \" quotes '"]];

Instead, you SHOULD do:

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @"this has \" lots of ' bizarre \" quotes '"];

All arguments provided to the -executeUpdate: method (or any of the variants that accept a va_listas a parameter) must be objects. The following will not work (and will result in a crash):

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];

The proper way to insert a number is to box it in an NSNumber object:

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];

Alternatively, you can use the -execute*WithFormat: variant to use NSString-style substitution:

[db executeUpdateWithFormat:@"INSERT INTO myTable VALUES (%d)", 42];

Internally, the -execute*WithFormat: methods are properly boxing things for you. The following percent modifiers are recognized: %@%c%s%d%D%i%u%U%hi%hu%qi%qu%f,%g%ld%lu%lld, and %llu. Using a modifier other than those will have unpredictable results. If, for some reason, you need the % character to appear in your SQL statement, you should use %%.

Using FMDatabaseQueue and Thread Safety.

Using a single instance of FMDatabase from multiple threads at once is a bad idea. It has always been OK to make a FMDatabase object per thread. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. Bad things will eventually happen and you'll eventually get something to crash, or maybe get an exception, or maybe meteorites will fall out of the sky and hit your Mac Pro. This would suck.

So don't instantiate a single FMDatabase object and use it across multiple threads.

Instead, use FMDatabaseQueue. It's your friend and it's here to help. Here's how to use it:

First, make your queue.

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

Then use it like so:

[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]) {

}
}];

An easy way to wrap things up in a transaction can be done like this:

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

FMDatabaseQueue will run the blocks on a serialized queue (hence the name of the class). So if you call FMDatabaseQueue's methods from multiple threads at the same time, they will be executed in the order they are received. This way queries and updates won't step on each other's toes, and every one is happy.

Note: The calls to FMDatabaseQueue's methods are blocking. So even though you are passing along blocks, they will not be run on another thread.

iOS之Sqlite和FMDB的更多相关文章

  1. iOS开发--SQLite重要框架FMDB的使用

    什么是FMDB: FMDB是一个和iOS的SQLite数据库操作相关的第三方框架.主要把C语言操作数据库的代码用OC进行了封装.使用者只需调用该框架的API就能用来创建并连接数据库,创建表,查询等. ...

  2. iOS开发数据库篇—FMDB简单介绍

    iOS开发数据库篇—FMDB简单介绍 一.简单说明 1.什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的方式封装了SQLite的C语言API 2.FMDB的优点 使用起来 ...

  3. iOS开发数据库篇—FMDB数据库队列

    iOS开发数据库篇—FMDB数据库队列 一.代码示例 1.需要先导入FMDB框架和头文件,由于该框架依赖于libsqlite库,所以还应该导入该库. 2.代码如下: // // YYViewContr ...

  4. 在iOS开发中使用FMDB

    在iOS开发中使用FMDB 前言 SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库.iOS SDK 很早就支持了 SQLite,在使用时,只需 ...

  5. iOS中SQLite知识点总结2

    数据库(SQLite) 01-多表查询 格式:select 字段1,字段2,... from 表名1,表名2; 别名:select 别名1.字段1 as 字段别名1,别名2.字段2 as 字段别名2, ...

  6. iOS 数据库操作(使用FMDB)

    iOS 数据库操作(使用FMDB)   iOS中原生的SQLite API在使用上相当不友好,在使用时,非常不便.于是,就出现了一系列将SQLite API进行封装的库,例如FMDB.Plausibl ...

  7. [iOS]数据库第三方框架FMDB详细讲解

    [iOS]数据库第三方框架FMDB详细讲解 初识FMDB iOS中原生的SQLite API在进行数据存储的时候,需要使用C语言中的函数,操作比较麻烦.于是,就出现了一系列将SQLite API进行封 ...

  8. 【转】在iOS开发中使用FMDB

    本文转载自:唐巧的博客 在iOS开发中使用FMDB APR 22ND, 2012 前言 SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库.iO ...

  9. 我为什么用 SQLite 和 FMDB 而不用 Core Data

    凭良心讲,我不能告诉你不去使用Core Data.它不错,而且也在变好,并且它被很多其他Cocoa开发者所理解,当有新人加入你的组或者需要别人接手你的项目的时候,这点很重要.更重要的是,不值得花时间和 ...

随机推荐

  1. layer父页面刷新

    $.ajax({ type:"POST", url:"{sh::U('Address/edit')}", data: $("#saveform&quo ...

  2. bzoj3165 1568

    1568是3165的弱化版,发的代码是3165的这道题完全没想出来,是看wyl大神的题解http://hi.baidu.com/wyl8899/item/2deafd3a376ef2d46d15e99 ...

  3. poj3037

    首先到每个点的速度实际上是一个定值,就是v0*2^(起点与当前点高度差) 所以当前点i到任意一个相邻点的时间都是一个定值, 不难想到构图最短路径 ..] ,,,);       dy:..] ,,-, ...

  4. BZOJ_1616_[Usaco2008_Mar]_Cow_Travelling_游荡的奶牛_(DP)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1616 给出一张图,有些点不能走,给出起始点和结束点,以及时间,求在该时间到达结束点的方案数. ...

  5. 如何判断 Android 应用的 Apk 签名是否一致?

    可以比对apk签名的fingerprint. 假定安装了JDK,如果想查HelloWorld.apk所使用的签名的fingerprint,可以这样做: 1. 查找apk里的rsa文件 (Windows ...

  6. HDU 3416 Marriage Match IV dij+dinic

    题意:给你n个点,m条边的图(有向图,记住一定是有向图),给定起点和终点,问你从起点到终点有几条不同的最短路 分析:不同的最短路,即一条边也不能相同,然后刚开始我的想法是找到一条删一条,然后光荣TLE ...

  7. 分割函数和根据Id串返回名字

    需求:函数传入一个字符串参数 例如  123-456 将这个字符串123-456拆成两个值 123   456,在通过两个值分别查出数据(例如 张三  李四),拼接成     张三-李四 --声明变量 ...

  8. Unity光照图UV显示

    美术的同学觉得 Unity 光照图烘焙的不够美丽,需要在 ps 里修一修,但是不知道每个物体对应的光照图在哪个区域,UV 是如何分布的,于是要求写一个工具显示,于是有了下面这个: 打开场景自动读取当前 ...

  9. [codevs3295]落单的数

    题目描述 Description 有n个数(n是奇数),其中n-1个数两两成对,有1个数落单,找出这个数.要求O(n)的时间复杂度,O(1)的空间复杂度 输入描述 Input Description ...

  10. bzoj 1228 [SDOI2009]E&D(sg函数,找规律)

    Description 小E 与小W 进行一项名为“E&D”游戏.游戏的规则如下:桌子上有2n 堆石子,编号为1..2n.其中,为了方便起见,我们将第2k-1 堆与第2k 堆(1 ≤ k ≤ ...