您的位置:首页 > 移动开发 > IOS开发

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)下的使用 :

注意: 创建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;
}
}];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: