数据存储 —FMDB
一、FMDB简介
iOS中原生的SQLite API在进行数据存储的时候,需要使用C语言中的函数,操作比较繁琐。于是,就出现了一系列将SQLite API进行封装的库,例如FMDB、PlausibleDabase、SQLitePersistentObjects等。
FMDB是一款简洁、易用的封装库。因此,在这里推荐使用第三方框架FMDB,它是对lisqlite3框架的封装,用起来的步骤与SQLite使用类似,并且它对于多线程的并发操作进行了处理,所以是线程安全的。
FMDB优缺点
优点:
1、对多线程的并发操作进行处理,所以是线程安全的;
2、以OC的方式封装了SQLite的C语言API,使用起来更加的方便;
3、FMDB是轻量级的框架,使用灵活。
缺点:
1、因为它是OC的语言封装的,只能在iOS开发的时候使用,所以在实现跨平台操作的时候存在局限性。
FMDatabase:一个FMDatabase对象就代表一个单独的SQLite数据库,用来执行SQL语句。
FMResultSet:使用FMDatabase执行查询后的结果集。
FMDatabaseQueue:用于在多线程中执行多个查询或更新,它是线程安全的。
FMDB使用步骤
1.下载FMDB文件(gitHub链接:https://github.com/ccgus/fmdb),并将FMDB文件夹添加到项目中(也可以使用CocoaPods导入)
2.导入lisqlite3.0框架,导入头文件FMDatabase.h
3.代码实现,与SQLite使用步骤相似,创建数据库路径没货的数据库路径,打开数据库,然后对数据库进行增、删、改、查操作,最后关闭数据库。
二、FMDB创建数据库和数据表
创建FMDatabsae对象时参数为SQLite数据库文件路径,该路径可以是以下三种方式之一
1、文件路径。该文件路径无需真是存在,如果不存在会自动创建
2.空字符串(@“”)。表示会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,文件也会被删除;
3.NULL。将创建一个内咋数据库,同样的,当FMDatabase连接关闭时,数据将会被销毁
FMDB—数据库创建
建立数据库
//1.获得数据库文件的路径
NSString *doc = [NSSEarchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)LastObject];
NSString *fileName = [doc stringByAppendingPathComponent:@“student.sqlite”];
//2.获得数据库
FMDatabase *db = [FMDatabase databaseWithPath:fieName];
//3.在和数据库交互之前,数据库必须是打开的,如果权限不足或者资源不足,则无法打开和创建数据库。
if([db open] ){
//创建表
}
//4.创建表
if([db open]) {
BOOL result = [db executeUpdate:@“CREATE TABLE IF NOT EXISTS t_student (id integer PRIMARY KEY AUTOINCREMENT ,name text NOT NULL,age integer NOT NULL);”];
if (result) {
NSL(@“创建表成功”);
}
关闭数据库
[db close];
}
三、FMDB实现增、删、改、查
FMDB - 执行更新
一切不是SELECT命令的命令都视为更新。这包括CREAT,UPDATE,INSERT,ALTER,BEGIN,COMMIT,DETACH,DELETE,DROP,END,EXPLAI,VACUUM,REPLACE等。
简单来说,只有不是以SELECT开头的命令都是更新命令。
执行更新返回一个BOOL值。YES表示执行成功,否则表示有错误。你可以调用-lastErrorMessage和-lastErrorCode方法来得到更多信息。
int age = 42;
//1.executeUpdate:不确定的参数用?来占位(后面参数必须是oc对象。:代表语句结束)
[self.db executeUpdate:@“INSERT INTO t_student(name,age)VALUES(?,?),name,@(age)”];
//2.executeUpdateWithFormat:不确定的参数用%@,%d等来占位(参数为原始数据类型,执行语句不区分大小写)
[self.db executeUpdateithFormat:@“insert into t_student(name,age)values(%@,%i);”,name,age];
//3.数组
[self.db executeUpdate:@“INSERT INTO t_student(name,age)VALUES(?,?);” withArgumentInarray:@[name,@(age)]];
FMDB —删除数据
//1.不确定的参数用?来占位(后面参数必须是oc对象)
[self.db executeUpdate:@“delete from t_student where id = ?;”,@(idNum)];
//2.不确定的参数用%@,%d等来占位
[self.db executeUpdateWithFormat:@“delete from t——student where 那么= %@;“,@”apple_name“];
//如果表格存在 则销毁
[self.db execyteUpdate:@“drop table if exists t_student;”];
FMDB—修改数据
//修改学生的名字
[self.db executeUpdate:@“update t_student set name = ? where name = ?”,newName,oldName];
FMDB—查询数据
1、SELECT命令就是查询,执行查询的方法是以ececuteQuery开头的。
2、执行查询时,如果成功返回FMResultSet对象,错误返回nil。与指向更新相同,支持使用NSError参数。
3、同时,你也可以使用-lasErrorCode和-lastErrorMessage获知错误信息
intForColumn
longForColumn
longLongIntForColumn
boolForColumn
doubleForColumn
stringForColumn
dataForColumn
dataNoCopyForColumnIndex
UTF8StringForColumnIndex
objectForColumn
FMDB—查询数据
//1.执行查询语句
//查询整个表
FMResult *result = [self.db executeQuery:@”select *from t_student;“];
//根据条件查询
FMResultSet *resultSet = [self.db executeQuery:@“select *from t_student where id<?;”,@(14)];
//遍历结果集合
while ([resultSet next]) {
int idNum = [resultSet intForColumn:@“id”];
NSString *name = [resultSet objectForColumn:@“name”];
int age = [resultSet intForColumn:@“age”];
}
四、FMDB实现多线程操作
FMDB - 多线程下的使用
FMDatabase实例能否在多线程中使用
如果应用中使用了多线程操作数据库,那么久需要使用FMDatabaseQueue来保证线程安全了。应用中不可再多个线程中共同使用一个FMDatabase对象作数据库,这样会引起数据库数据混乱(例如使用两个线程同时对数据库进行更新和查找)。为了多线程操作数据库安全,FMDB使用了FMDatabaseQueue。
多线程更新相同的资源导致数据竞争会死使用等待队列(等待现在执行的处理结束).
//1.创建队列
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
__block BOOl whoopsSomethingWrongHappened = true;
//2.把任务包装到事务里
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
//串行队列
isSucceed = [db executeUpdate:@"insert into t_student(name,age,sex) values (?,?,?)",@"GBLW",@"38",@"男"] && isSucceed;
isSucceed = [db executeUpdate:@"insert into t_student(name,age,sex)values(?,?,?)",@"-1",@"438",@"未知"] && isSucceed;
isSucceed = [db executeUpdate:@"insert into t_student(name,age,sex)values(?,?,?)",@"AJAR",@"18",@"男"] && isSucceed;
//如果有错误,就会将它返回
if (!isSucceed) {
//block返回的参数rollback进行处理(bool类型的指针)
*rollback = YES;
return ;
}
}];
//关闭数据库
[self.dataBase close];
}
//布局button
#import "ViewController.h" //第一步:引入框架,引入支持类库(libsqlite3.0tbd) #import <FMDB.h> @interface ViewController () ///声明数据库对象 @property(nonatomic,strong)FMDatabase *dataBase; ///声明存储路径 @property(nonatomic,copy)NSString *filePath; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //创建表 [self creatTable]; unsigned char i = 0; i = ~i; NSLog(@"i=%c",i); } #pragma mark - 创建表 - (void)creatTable { //第一步:创建sql语句 NSString *createSql = @"create table if not exists t_student(id integer primary key autoincrement not null ,name text not null,age integer not null,sex text not null)"; //第二步:找到存储路径 NSString *document = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]; NSLog(@"document = %@",document); self.filePath = [document stringByAppendingPathComponent:@"student.sqlite"]; NSLog(@"filePath = %@",self.filePath); //第三步:使用路径初始化DMDB对象 self.dataBase = [FMDatabase databaseWithPath:self.filePath]; //第四步:数据库执行相关的操作 //需要判断数据库打开的时候才进行执行语句 if ([self.dataBase open]) { BOOL result = [self.dataBase executeUpdate:createSql]; if (result) { NSLog(@"创建表成功"); }else { NSLog(@"创建表失败"); } } //第五步:关闭数据库 [self.dataBase close]; } #pragma mark - 添加学生 - (IBAction)insertIntoAction:(id)sender { //第一步:打开数据库 [self.dataBase open]; //第二步:进行相关的操作 NSArray * nameArr = [NSArray arrayWithObjects:@"MBBoy",@"炸天",@"小明", nil]; for (int i = 0; i < nameArr.count; i++) { NSString *name = [nameArr objectAtIndex:i]; //插入语句 // NSString *insertSql = @"insert into t_student(name,age,sex) values(?,?,?)"; BOOL result = [self.dataBase executeUpdate:@"insert into t_student(name,age,sex) values(?,?,?)",name,@"69",@"男"]; if (result) { NSLog(@"插入成功"); }else { NSLog(@"插入失败"); NSLog(@"result = %d",result); } } //关闭数据库 [self.dataBase close]; } #pragma MARK - 更改学生 - (IBAction)updeAction:(id)sender { //打开数据库 [self.dataBase open]; BOOL result = [self.dataBase executeUpdate:@"update t_student set name = ? where name = ?",@"孟令旭",@"MBBoy"]; if (result) { NSLog(@"更改成功"); }else { NSLog(@"更改失败"); } [self.dataBase close]; } #pragma mark - 删除学生 - (IBAction)deleteAction:(id)sender { //打开数据库 [self.dataBase open]; //执行sql语句 BOOL result = [self.dataBase executeUpdate:@"delete from t_student where name = ?",@"孟令旭"]; if (result) { NSLog(@"删除成功"); }else { NSLog(@"删除失败"); } //关闭数据库 [self.dataBase close]; } #pragma mak - 查询学生 - (IBAction)searchAction:(id)sender { //查询表中的所有数据 [self.dataBase open]; //查询结果使用的类FMResultSet FMResultSet *resultSet = [self.dataBase executeQuery:@"select * from t_student"]; //遍历出需要的结果内容 while ([resultSet next]) { NSString * name = [resultSet objectForColumnName:@"name"]; NSInteger age = [resultSet intForColumn:@"age"]; NSString *sex = [resultSet objectForColumnName:@"sex"]; NSLog(@"name = %@,age = %ld,sex = %@",name,age,sex); } //关闭数据库 [self.dataBase close]; } #pragma mark - 插入很多学生 - (IBAction)insertManyStudents:(id)sender { //以队列的形式添加数据是FMDB比较常用的添加方式 //FMDB不支持多个线程同时操作,所以一般以串行的方式实现相关的操作 //第一步:创建操作队列 //打开数据库 [self.dataBase open]; FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:self.filePath]; //标识:记录是否操作成功 __block BOOL isSucceed = YES; //第二步:把所需要的事件打包放在操作队列中 [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { //串行队列 isSucceed = [db executeUpdate:@"insert into t_student(name,age,sex) values (?,?,?)",@"GBLW",@"38",@"男"] && isSucceed; isSucceed = [db executeUpdate:@"insert into t_student(name,age,sex)values(?,?,?)",@"-1",@"438",@"未知"] && isSucceed; isSucceed = [db executeUpdate:@"insert into t_student(name,age,sex)values(?,?,?)",@"AJAR",@"18",@"男"] && isSucceed; //如果有错误,就会将它返回 if (!isSucceed) { //block返回的参数rollback进行处理(bool类型的指针) *rollback = YES; return ; } }]; //关闭数据库 [self.dataBase close]; } @end