FMDB

来源:互联网 发布:apply js 编辑:程序博客网 时间:2024/05/22 06:25

FMDB

一、FMDB简介

FMDB是iOS平台的SQLite数据库框架,它以OC的方式封装了SQLite的C语言API,github地址是https://github.com/ccgus/fmdb。

FMDB的优点:
1. 使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码
2. 对比苹果自带的Core Data框架,更加轻量级和灵活
3. 提供了多线程安全的数据库操作方法,有效地防止数据混乱

FMDB有三个主要的类
1. FMDatabase。一个FMDatabase对象就代表一个单独的SQLite数据库,用来执行SQL语句
2. FMResultSet。使用FMDatabase执行查询后的结果集。
3. FMDatabaseQueue。用于在多线程中执行多个查询或更新,它是线程安全的。

在FMDB中,除查询以外的所有操作,都称为“更新”,包括:create、drop、insert、update、delete等。

二、FMDatabase

使用前,需要下载FMDB框架,并导入src下的fmdb文件夹。之后还需要在项目中添加sqlite3.dylib,并在程序文件中导入FMDB.h。

2.1 创表

获取到sqlite文件路径path之后:

@property (nonatomic, strong) FMDatabase *db;//创建数据库实例对象self.db = [FMDatabase databaseWithPath:path];//打开数据库if (![self.db open]) {    NSLog(@"打开数据库失败");}else {    //创表student    BOOL result = [self.db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text unique, age integer not null);"];    if (result) {        NSLog(@"创表成功");    }else {        NSLog(@"创表失败");    }}

2.2 增删改查

//插入数据BOOL result = [self.db executeUpdate:@"insert into t_class (name) values(?)", @"Steven"];//删除数据,注意int等基本数据类型需要转为NSNumberBOOL result1 = [self.db executeUpdate:@"delete from t_student where id = ", @10];//更改数据BOOL result2 = [self.db executeUpdate:@"update t_student set ame = ? where id = ?", @"Sally", @100];//查询数据//注意查询和增删改、创表等操作都不一样,它执行的executeQuery方法FMResultSet *resultSet = [self.db executeQuery:@"select * from t_student where id = ? or id = ?", @10, @11];while (resultSet.next) {    int ID = [resultSet intForColumn:@"ID"];    NSString *name = [resultSet stringForColumn:@"name"];    int age = [resultSet intForColumn:@"age"];    NSLog(@"%d, %@, %d", ID, name, age);}

2.3 外键、count

和使用原声的SQLite API一样,在FMDB中使用外键同样需要使用代码开启,不一样的是,FMDB中只需要在打开数据库的时候开启一次即可。在上一篇博客中介绍了多表关联、级联更新等内容,这里不再介绍,直接贴上开启外键的语句即可。

//开启外键BOOL openfk = [self.db executeUpdate:@"PRAGMA foreign_keys = ON"];

计数:

int count = 0;FMResultSet *resultSet = [self.db executeQuery:@"select count(*) from t_student where id > 8"];if (resultSet.next) {    count = [resultSet intForColumnIndex:0];    NSLog(@"count-->%d", count);}

三、FMDatabaseQueue

FMDatabase这个类是线程不安全的,如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题。为了保证线程安全,FMDB提供方便快捷的FMDatabaseQueue类。

3.1 创表

获取到数据库文件路径path后:

@property (nonatomic, strong) FMDatabaseQueue *queue;    //创建数据库队列    //数据库同时也会被创建并打开    self.queue = [FMDatabaseQueue databaseQueueWithPath:path];    //创建表    [self.queue inDatabase:^(FMDatabase *db) {        //创表student        BOOL result = [db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer);"];        if (result) {            NSLog(@"创表成功");        }else {            NSLog(@"创表失败");        }    }];

3.2 查询数据

增删改查的使用方法和创表差不多,这里只贴上查询数据的方法:

    [self.queue inDatabase:^(FMDatabase *db) {        FMResultSet *resultSet = [db executeQuery:@"select * from t_student where id = ?", @1];        while (resultSet.next) {            NSString *name = [resultSet stringForColumn:@"name"];            int age = [resultSet intForColumn:@"age"];            NSLog(@"name-->%@, age-->%d", name, age);        }    }];

四、测试FMDatabase和FMDatabaseQueue

FMDatabaseQueue是线程安全的,但光这么说难以形象的知道它和FMDatabase存在的差异,因此特地写几行代码测试一下。

    dispatch_queue_t q = dispatch_queue_create("testUpdate", DISPATCH_QUEUE_CONCURRENT);    for (int i=0; i<10; i++) {        dispatch_async(q, ^{        //测试FMDatabaseQueue时,把里面的代码放进[self.queue inDatabase:^(FMDatabase *db) { }];里面执行//            [self.queue inDatabase:^(FMDatabase *db) {            BOOL result1 = [self.db executeUpdate:@"update t_student set name = ? where id = ?", @"Alice", @10];            BOOL result2 = [self.db executeUpdate:@"update t_student set name = ? where id = ?", @"Sally", @10];            BOOL result3 = [self.db executeUpdate:@"update t_student set name = ? where id = ?", @"Steven", @10];            BOOL result4 = [self.db executeUpdate:@"update t_student set name = ? where id = ?", @"Hayley", @10];            if (result1) {                NSLog(@"%@-----result1更新成功", [NSThread currentThread]);            }else {                NSLog(@"%@-----result1更新失败, %@", [NSThread currentThread], [self.db lastErrorMessage]);            }            if (result2) {                NSLog(@"%@-----result2更新成功", [NSThread currentThread]);            }else {                NSLog(@"%@-----result2更新失败, %@", [NSThread currentThread], [self.db lastErrorMessage]);            }            if (result3) {                NSLog(@"%@-----result3更新成功", [NSThread currentThread]);            }else {                NSLog(@"%@-----result3更新失败, %@", [NSThread currentThread], [self.db lastErrorMessage]);            }            if (result4) {                NSLog(@"%@-----result4更新成功", [NSThread currentThread]);            }else {                NSLog(@"%@-----result4更新失败, %@", [NSThread currentThread], [self.db lastErrorMessage]);            }//          }];        });

上面的代码开启了一个并行序列,并异步执行里面的代码。

如果使用的是FMDatabase,打印结果如图:
FMDatabase

可以看到,程序运行期间会发生资源抢夺的问题,并且执行顺序是无序的。

换成FMDatabaseQueue,打印结果如图:
FMDatabaseQueue

可以看到,程序运行期间代码的执行是有序的,不会发生错误,并且每次执行均在同一个线程内。

进入FMDatabaseQueue.h文件,可以看到作者的注释如下:

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, t**hey 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.

@warning The calls to FMDatabaseQueue’s methods are blocking. So even though you are passing along blocks, they will **not** be run on another thread.

简单的说,就是作者把FMDatabaseQueue写死了,代码一定会在串行序列里执行,每次执行都只会创建一个线程,不会有多个线程同时抢夺同一个资源的问题发生。

五、事务

程序对数据库的操作不一定是独立的,有些时候,你会希望程序中的某些代码要么都执行成功,要么都执行失败。这种情况下,就应该使用事务。

5.1 使用方法(一)

    [self.db beginTransaction];    [self.db executeUpdate:@"update t_student set name = ? where id = ?", @"Alice3", @10];    //故意把name写成ame    BOOL result2 = [self.db executeUpdate:@"update t_student set ame = ? where id = ?", @"Sally", @100];    if (!result2){        NSLog(@"更新失败result2-->%@", [self.db lastErrorMessage]);        [self.db rollback];    }    [self.db executeUpdate:@"update t_student set name = ? where id = ?", @"Steven3", @11];    [self.db commit];

上面的代码中, 因为第二个更新操作是错误的,这时候程序执行rollback方法,所以前面第一个更新操作会被撤回,不更新。第3个更新操作之后未执行rollback方法,所以不撤销,更新成功。

5.2 使用方法(二)

在FMDatabaseQueue中,事务的使用方法如下

[self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {    BOOL result = [db executeUpdate:@"update t_student set name = ? where id = ?", @"Alice", @1];    if (result) {        NSLog(@"更新成功");    }else {        NSLog(@"更新失败, %@", [db lastErrorMessage]);        *rollback = YES;    }}];

六、总结

FMDB有三个主要的类
1. FMDatabase。一个FMDatabase对象就代表一个单独的SQLite数据库,用来执行SQL语句
2. FMResultSet。使用FMDatabase执行查询后的结果集。
3. FMDatabaseQueue。用于在多线程中执行多个查询或更新,它是线程安全的。

如果希望某些代码要么都执行成功,要么都执行失败,应该使用事务。

0 0
原创粉丝点击