关于FMDB/SQLite

+++
Categories = ["Database",]
Tags = ["iOS","FMDB","Database",]
date = "2014-07-18T21:35:38+08:00"
title = "关于FMDB/SQLite"

+++

到https://github.com/ccgus/fmdb 下载源文件,然后直接将fmdb文件夹拖入到你的工程就OK。
当然你需要添加依赖库:libsqlite3.dylib

拖入源文件,并且添加依赖库以后你就可以使用FMDB了,引用头文件
#import "FMDB.h"

USAGE

在FMDB中主要有三个类:

  1. FMDatabase - 简单的说这个类就是代表了数据库
  2. FMResultSet - 这个类表示查询操作的结果
  3. FMDatabaseQueue - 多线程操作的时候你会用到这个类,并且这是线程安全的。

创建数据库

你需要使用一个path来创建一个本地FMDatabase数据库,这个path有三种类型:

  1. 你可以使用一个本地的地址来创建这个数据库,这个地址不一定真实存在,如果不存在,那么FMDB会创建这个数据库并返回,存在则直接返回这个数据库。
  2. 一个空字符串@"".FMDB会在本地创建一个临时的数据库,当数据库关闭的时候会删除这个数据库。
  3. NULL.如果你将这个path填的是NULL,那么这个数据被创建在内存中,数据库关闭的时候被销毁。

(更多信息关于临时数据库和在内存中的数据库,你可以阅读这篇文档 http://www.sqlite.org/inmemorydb.html )

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

打开数据库

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

Executing Updates

所有不是select操作的操作都算update. 包括 CREATE, UPDATE, INSERT, ALTER, COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE ····换句话说也就是如果你的操作不是以SELECT开头的都是update操作.

update操作返回一个布尔值,YES表示操作成功,NO表示你可以遇到了一些错误.FMDatabase有两个方法 -lastErrorMessage 和 -lastErrorCode,你可以使用这两个方法来查看错误。

Executing Queries

SELECT 查询使用 -executeQuery... 方法.

查询成功返回 FMResultSet,失败则是返回nil.
同样你可以使用FMDatabase的两个方法 -lastErrorMessage and -lastErrorCode 来查找原因。

你需要用一个循环来获取到查询到的每一个值。like this:

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

FMResultSet 有许多类型用来返回不同类型的查询结果的值

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

以上的每个方法都有对应的 {type}ForColumnIndex: 上面那一溜方法是用例的名字来获取数据,而这个方法则是用数据在查询结果中对应的位置来获取数据.

Closing

用完了FMDB记得关闭

[db close];

批处理

FMDatabase的方法 executeStatements:withResultBlock:可以使用字符串来同时处理多条指令。

NSString *sql = @"create table test1 (id integer primary key autoincrement, x text);"
                 "create table test2 (id integer primary key autoincrement, y text);"
                 "create table test3 (id integer primary key autoincrement, z text);"
                 "insert into test1 (x) values ('XXX');"
                 "insert into test2 (y) values ('YYY');"
                 "insert into test3 (z) values ('ZZZ');";

success = [db executeStatements:sql];

sql = @"select count(*) as count from test1;"
       "select count(*) as count from test2;"
       "select count(*) as count from test3;";

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

数据处理

你必须使用标准的SQLite的标准语法,像下面那样(而不是SQL中那样):

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

‘?’这个符号表示插入数据的替代符,操作方法会接收参数来替代这个符号 (或者是代表这些参数的,比如说:NSArray, NSDictionary, va_list).

OC中你可以像下面这样使用:

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

Note:这里需要注意的是,如果是基本数据类型比如说NSInteger,你需要转化为NSNumber
如果是插入nil,那么你不能直接插入nil,而是需要插入[NSNull null],像上面那个例子中写的是:comment ?: [NSNull null],那么如果commit是nil的话则会插入nil,反之则会插入commit.

下面这种书写方法和上面表达的是同一个意思。

INSERT INTO authors (identifier, name, date, comment) VALUES (:identifier, :name, :date, :comment)

像上面这种写法参数是以冒号开头的,SQLite支持其他字符,但是在字典中key都是以冒号为前缀的,所以你的字典key中不要包含冒号

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

最关键的一点就是:千万不要用NSString的方法比如说 stringWithFormat来手动插入参数,必须要使用Values(?,?)这样的方法,把?当作替代符。

FMDatabaseQueue 是线程安全的

不要在多个线程之间使用同一个 FMDatabase对象,最好是每一个线程都有一个独立的 FMDatabase对象,如果你在多线程之间使用同一个对象,那么会有不好的事情发生。
如果你需要在多线程中使用 FMDatabase,那么请使用 FMDatabaseQueue,下面是他的使用方法:

首先创建你的线程

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

然后,像这样使用:

[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @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 (?)", @1];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3];

if (whoopsSomethingWrongHappened) {
    *rollback = YES;
    return;
}
// etc…
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @4];
}];

你可能感兴趣的:(关于FMDB/SQLite)