数据存储_FMDB数据库队列

Posted 青春-你好

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据存储_FMDB数据库队列相关的知识,希望对你有一定的参考价值。

一、代码示例

1.需要先导入FMDB框架和头文件,由于该框架依赖于libsqlite库,所以还应该导入该库。

2.代码如下:

复制代码
 1 //
 2 //  YYViewController.m
 3 //  05-FMDB数据库队列
 4 //
 5 //  Created by apple on 14-7-28.
 6 //  Copyright (c) 2014年 wendingding. All rights reserved.
 7 //
 8 
 9 #import "YYViewController.h"
10 #import "FMDB.h"
11 
12 @interface YYViewController ()
13 @property(nonatomic,strong)FMDatabaseQueue *queue;
14 @end
15 
16 @implementation YYViewController
17 
18 - (void)viewDidLoad
19 {
20     [super viewDidLoad];
21     
22     //1.获得数据库文件的路径
23     NSString *doc=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
24     NSString *fileName=[doc stringByAppendingPathComponent:@"person.sqlite"];
25     
26     //2.获得数据库队列
27     FMDatabaseQueue *queue=[FMDatabaseQueue databaseQueueWithPath:fileName];
28 //    FMDatabase *db=[FMDatabase databaseWithPath:fileName];
29     
30     //3.打开数据库
31     [queue inDatabase:^(FMDatabase *db) {
32          BOOL result=[db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
33         if (result) {
34             NSLog(@"创表成功");
35         }else
36         {
37             NSLog(@"创表失败");
38         }
39     }];
40     self.queue=queue;
41     
42 }
43 
44 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
45 {
46     //插入数据
47 //    [self.queue inDatabase:^(FMDatabase *db) {
48 //        [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @22];
49 //    }];
50     
51     //查询数据
52     [self.queue inDatabase:^(FMDatabase *db) {
53         // 1.执行查询语句
54         FMResultSet *resultSet = [db executeQuery:@"SELECT * FROM t_person"];
55 
56         // 2.遍历结果
57         while ([resultSet next]) {
58             int ID = [resultSet intForColumn:@"id"];
59             NSString *name = [resultSet stringForColumn:@"name"];
60             int age = [resultSet intForColumn:@"age"];
61             NSLog(@"%d %@ %d", ID, name, age);
62         }
63     }];
64 
65 }
66 
67 @end
复制代码

先插入数据,之后查询结果,打印如下:

3.代码说明

有了一个队列对象,它的内部自动就拥有一个数据库对象,且数据库的操作是线程安全的。
 
二、事务
事务,没有事务的话会出现问题。
举例:银行的例子
 
张三和李四账户都有1000块钱,如果张三要转账给李四,需要执行两条SQL语句,考虑到安全性,要求这两条语句要么全部执行成功,要么全部执行失败。
事务:把多条语句放到同一个事务中,要么全部成功,要么全部失败(如果中途出现问题,那么会自动回滚)。事务的执行具有原子性。
  事务代码处理:
  把多条语句添加到一个事务中去执行:
复制代码
1    //插入数据
2     [self.queue inDatabase:^(FMDatabase *db) {
3         [db beginTransaction];
4         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @22];
5         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @23];
6         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @24];
7         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @25];
8         [db commit];
9     }];
复制代码

如果中途出现问题,那么会自动回滚(重新开始),也可以选择手动回滚。

复制代码
 1     //插入数据
 2     [self.queue inDatabase:^(FMDatabase *db) {
 3         [db beginTransaction];
 4         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @22];
 5         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @23];
 6         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @24];
 7         [db rollback];
 8         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @25];
 9         [db commit];
10     }];
复制代码

上面的代码。前三条插入语句是作废的。

事务处理的另一种方式:

1    [self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
2         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @22];
3         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @23];
4         [db executeUpdate:@"INSERT INTO t_person (name, age) VALUES (?, ?);",@"wendingding", @24];
5     }];

 

具体可以参考FMDB官方文档:

使用FMDatabaseQueue 及线程安全
在多个线程中同时使用一个FMDatabase实例是不明智的。现在你可以为每个线程创建一个FMDatabase对象。 不要让多个线程分享同一个实例,它无法在多个线程中同时使用。所以,不要初始化FMDatabase对象,然后在多个线程中使用。请使用 FMDatabaseQueue,它是你的朋友而且会帮助你。以下是使用方法:
 
首先创建队列。
  1. FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; 
这样使用(简单的使用,非事务)
  1.  [queue   inDatabase:^(FMDatabase *db) {    
  2.           [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];    
  3.           [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];    
  4.           [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];    
  5.           FMResultSet *rs = [db executeQuery:@"select * from foo"];    
  6.          while([rs next]) {   
  7.             …    
  8.          }    
  9. }];   
像这样,轻松地把简单任务包装到事务里:
  1. [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {    
  2.             [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];    
  3.             [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];    
  4.             [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];    
  5.             if (whoopsSomethingWrongHappened) {    //有什么错误发生的话,回滚
  6.                     *rollback = YES; return;    
  7.             }   
  8.             // etc…    
  9.             [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];    
  10.     }];   
 
 FMDatabaseQueue  后台会建立系列化的G-C-D队列,并执行你传给G-C-D队列的块。这意味着 你从多线程同时调用调用方法,GDC也会按它接收的块的顺序来执行。
 

whoopsSomethingWrongHappened是什么鬼?????看一下下面的demo

#pragma mark - 数据存储文件的路径

- (NSString *)dbPathTest {

    NSString *string = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

    NSString *stringPath = [string stringByAppendingPathComponent:@"test.sqlite"];

    NSLog(@"%@",stringPath);

    return stringPath;

}

 #pragma mark - 多线程操作数据库时,事务的处理方式

- (void)threadTransaction {

    //创建队列

    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:[self dbPathTest]];

    __block BOOL whoopsSomethingWrongHappened = true;

    //把任务包装到事务里

    [queue  inTransaction:^(FMDatabase *db, BOOL *rollback) {

        whoopsSomethingWrongHappened &= [db executeUpdate:@"insert into myTable values(?)",[NSNumber numberWithInt:1]];

        whoopsSomethingWrongHappened &= [db executeUpdate:@"insert into myTable values(?)",[NSNumber numberWithInt:2]];

        whoopsSomethingWrongHappened &= [db executeUpdate:@"insert into myTabl values(?)",[NSNumber numberWithInt:3]];

        //如果有错误 返回

        if(!whoopsSomethingWrongHappened) {

            *rollback = YES;

            return;

        }

         NSLog(@"hello,yoowei");

    }];

}

 

还可以看一下其他的demo中的写法:

- (void)saveChangesInBackground:(void (^)())complete {

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:self.schema.storage];

        [queue  inTransaction:^(FMDatabase *db, BOOL *rollback) {

            @try {

                for (FMDTObject *entry in self.dataArray) {

                    NSArray *array = [self getObjectValues:entry];

                    NSString *statement = nil;

                    if (self.relpace) {

                        statement = self.schema.statementReplace;

                    } else {

                        statement = self.schema.statementInsert;

                    }

                    [db executeUpdate:statement withArgumentsInArray:array];

                }

            }

            @catch (NSException *exception) {

                *rollback = YES;

            }

            [self.dataArray removeAllObjects];

            

            if (complete) {

                complete();

            }

        }];

    });

  }

 

- (void)threadNoTransaction {

    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:[self dbPathTest]];

    [queue inDatabase:^(FMDatabase *db) {

        [db beginTransaction];

        BOOL isRollBack = NO;

        @try {

            for (int i = 0; i<5; i++) {

                NSString *roomID= [[NSString alloc] initWithFormat:@"roomid_%d",i];

                NSString *sql= @"insert into myTable values(?)";

                BOOL a = [db executeUpdate:sql,roomID];

                if (!a) {

                    NSLog(@"插入失败1");

                }

            }

        }

        @catch (NSException *exception) {

            isRollBack = YES;

            [db rollback];

        }

        @finally {

            if (!isRollBack) {

                [db commit];

            }

        }

    }];

}

 

 

参考链接:

http://www.cnblogs.com/wendingding/p/3873874.html

https://github.com/ccgus/fmdb

以上是关于数据存储_FMDB数据库队列的主要内容,如果未能解决你的问题,请参考以下文章

FMDB数据库队列

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

iOS Fmdb数据存储

使用多线程读取 FMDB 数据库

FMDB之数组字典的存储

使用 fmdb 无法更新 blob 类型数据