iOS之FMDB的基本使用
2016-05-06 22:06
330 查看
优点 :
对多线程的并发操作进行处理,所以是线程安全的
以OC的方式封装了SQLite的C语言API,使用起来更加的方便;
FMDB是轻量级的框架,使用灵活
缺点:
因为它是OC的语言封装的,只能在iOS开发的时候使用, 所以在实现跨平台操作的时候存在局限性
FMDB中重要的类
FMDatabase : 一个FMDatabase对象就代表一个单独的SQLite数据库, 用来执行SQL语句
FMResultSet : 使用FMDatabase执行查询的结果集
FMDatabaseQueue : 用于在多线程中执行多个查询或更新,它是线程安全的
FMDB数据库(SQLite)下的使用 :
FMDB多线程下的使用
如果应用中使用了多线程操作数据库, 那么就需要使用FMDatabaseQueue来保证线程安全. 应用中不可再多个线程中公共使用一个FMDatabase对象操作数据库,这样会引起数据库数据混乱(例如,使用两个线程同时对数据库进行更新和查找). 为了多线程操作数据库安全, FMDB使用了FMDatabaseQueue.
多个线程更新相同的资源导致数据竞争时使用等待队列(等待现在执行的处理结束)
对多线程的并发操作进行处理,所以是线程安全的
以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/FMDB.h> 代码实现: (对FMDB中数据库的增删改查的简单封装)
#import "FMDBHelp.h" #import <FMDB/FMDB.h> @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 <Foundation/Foundation.h> @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; } }]; }