iOS之FMDB的基本使用

  • 优点 :
    • 对多线程的并发操作进行处理,所以是线程安全的
    • 以OC的方式封装了SQLite的C语言API,使用起来更加的方便;
    • FMDB是轻量级的框架,使用灵活
  • 缺点:
    • 因为它是OC的语言封装的,只能在iOS开发的时候使用, 所以在实现跨平台操作的时候存在局限性

FMDB中重要的类

  • FMDatabase : 一个FMDatabase对象就代表一个单独的SQLite数据库, 用来执行SQL语句
  • FMResultSet : 使用FMDatabase执行查询的结果集
  • FMDatabaseQueue : 用于在多线程中执行多个查询或更新,它是线程安全的

FMDB数据库(SQLite)下的使用 :

 注意: 创建FMDatabase对象时参数为SQLite数据库文件路径, 该路径可以是一下三种方式之一

- 文件路径.该文件路径无需真是存在,如果不存在会自动创建
- 空字符串(@“”).  表示会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,文件也会被删除
- NULL.  将创建一个内在数据库, 同样的, 当FMDatabase连接关闭时, 数据将会被摧毁


 首先要导入libsqlite3.0框架,导入头文件 import 

 代码实现:
      (对FMDB中数据库的增删改查的简单封装)
#import "FMDBHelp.h"
#import 
@interface FMDBHelp ()

@property(nonatomic,strong)NSString *fileName;//数据库文件的路径
@property(nonatomic,strong)FMDatabase *database; //数据库对象

@end

@implementation FMDBHelp
#pragma mark - 单例
+ (FMDBHelp*)sharedFMDBHelp {
    static FMDBHelp *help = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        help = [[FMDBHelp alloc] init];
    });
    return help;
}

#pragma mark - 让用户来命名数据库的名称
- (void)createDBWithName:(NSString*)dbName {
    if (dbName.length == 0) {
        //是防止用户直接传值为nil 或者 NULL
        self.fileName = @"";
    } else {
        //判断用户是否为数据库文件添加后缀名
        if ([dbName containsString:@".sqlite"]) {
            self.fileName = dbName;
        } else {
            self.fileName = [dbName stringByAppendingString:@".sqlite"];
        }
    }
}

#pragma amrk - 根据名称创建沙盒路径用来保存数据库文件
- (NSString*)dbPath {
    //说明fileName不为空
    if (self.fileName.length) {
        //获取沙盒主路径
        NSString *homePath = NSHomeDirectory();
        //完整路径
        NSString *savePath = [homePath stringByAppendingPathComponent:[NSString stringWithFormat:@"documents/%@",self.fileName]];
        NSLog(@"%@",savePath);
        return savePath;
    } else {
        return @"";
    }
}

#pragma mark - 创建数据库对象
//懒加载
- (FMDatabase*)database {
    if (!_database) {
        _database = [FMDatabase databaseWithPath:[self dbPath]];
    }
    return _database;
}

#pragma mark - 打开或者创建数据库
- (BOOL)openOrCreateDB {
    if ([self.database open]) {
        NSLog(@"数据库打开成功");
        return YES;
    } else {
        NSLog(@"数据库打开失败");
        return NO;
    }
}

#pragma mark - 无返回结果集的操作
- (BOOL)notResultSetWithSql:(NSString*)sql {
    //打开数据库
    BOOL isOpen = [self openOrCreateDB];
    if (isOpen) {
        //进行操作
        BOOL isSuccess = [self.database executeUpdate:sql];
        [self closeDB];
        NSLog(@"打开数据库成功");
        return isSuccess;
    } else {
        NSLog(@"打开数据库失败");
        return NO;
    }
}

#pragma mark - 关闭数据库的方法
- (void)closeDB {
    BOOL isClose = [self.database close];
    if (isClose) {
        NSLog(@"关闭数据库成功");
    } else {
        NSLog(@"关闭数据库失败");
    }
}

#pragma mark - 通用的查询方法
- (NSArray*)qureyWithSql:(NSString*)sql {
    //打开数据库
    BOOL isOpen = [self openOrCreateDB];
    if (isOpen) {
        //得到所有记录的结果集
        FMResultSet *set = [self.database executeQuery:sql];
        //声明一个可变数组,用来存放所有的记录
        NSMutableArray *array = [NSMutableArray array];
        //遍历结果集,取出每一条记录,将每一条记录转换为字典类型,并且存储到可变数组中
        while ([set next]) {
            //直接将一条记录转换为字典类型
            NSDictionary *dic = [set resultDictionary];
            [array addObject:dic];
        }
        //释放结果集
        [set close];
        [self closeDB];
        return array;
    } else {
        NSLog(@"打开数据库失败");
        return nil;
    }
}
#import 

@interface FMDBHelp : NSObject
+ (FMDBHelp*)sharedFMDBHelp;
//给数据库命名
- (void)createDBWithName:(NSString*)dbName;
//无返回结果集的操作
- (BOOL)notResultSetWithSql:(NSString*)sql;
//查询操作
- (NSArray*)qureyWithSql:(NSString*)sql;
@end

FMDB多线程下的使用

  • 如果应用中使用了多线程操作数据库, 那么就需要使用FMDatabaseQueue来保证线程安全. 应用中不可再多个线程中公共使用一个FMDatabase对象操作数据库,这样会引起数据库数据混乱(例如,使用两个线程同时对数据库进行更新和查找). 为了多线程操作数据库安全, FMDB使用了FMDatabaseQueue.
  • 多个线程更新相同的资源导致数据竞争时使用等待队列(等待现在执行的处理结束)
//    打开数据库
    [self openDB];
//    在子线程中执行数据库插入操作
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        [self threadNoTransaction];
    });

#pragma mark - 数据存储文件的路径
- (NSString *)dbPath {
    NSString *string = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *stringPath = [string stringByAppendingPathComponent:@"test.sqlite"];
    NSLog(@"%@",stringPath);
    return stringPath;
}

#pragma mark - 打开数据库并建表
- (void)openDB {
    FMDatabase *database = [FMDatabase databaseWithPath:[self dbPath]];
    //打开数据库,如果数据库打开,建表,如果失败就返回错误信息
    if([database open]) {
        //建表
       BOOL isSuccess = [database executeUpdate:@"create table if not exists stu(name text)"];
        if (isSuccess) {
            NSLog(@"建表成功");
        } else {
            NSLog(@"建表失败");
        }
    } else {
        NSLog(@"打开数据库失败");
    }
}

#pragma mark - 多线程操作数据库时,非事务的处理方式
// 事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。所以,应该把它们看成一个事务。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性

- (void)threadNoTransaction {
    //数据库文件的路径
    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:[self dbPath]];
    //将一组操作添加到非事务处理中
    [queue inDatabase:^(FMDatabase *db) {
        //将操作放入事务中,加入事务操作
        [db beginTransaction];

        BOOL isError = NO;
        int temp = -1;
        for (int i = 0; i < 10000; i++) {
            isError = [db executeUpdate:@"insert into stu values(?)",@(i)];
            if (!isError) {//说明inError == NO,插入有问题
                if(temp == -1) {
                    temp = i;
                }
            }
        }
        if (isError) {
            NSLog(@"所有插入动作成功");
        } else {
            NSLog(@"所有插入动作失败---%d",temp);
        }
        //提交事务
        [db commit];
    }];
}

#pragma mark - 多线程操作数据库时,事务的处理方式
- (void)threadTransaction {
    //创建队列
    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:[self dbPath]];
    __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 myTable values(?)",[NSNumber numberWithInt:3]];
        //如果有错误 返回
        if(!whoopsSomethingWrongHappened) {
            *rollback = YES;
            return;
        }
    }];
}

你可能感兴趣的:(iOS)