课堂分享之FMDB

  • The only limit is your imagination. 唯一的局限是你的想象力。
我是新手,一个不折不扣的新手,永远在追赶别人,此次出写,就是综合下班上的中午分享,和大神们的博客,有问题希望大家说出来,一起探讨,致谢!

FMDB同时兼容ARC和非ARC工程,会自动根据工程配置来调整相关的内存管理代码。

FMDB有三个主要的类
1.FMDatabase – 表示一个单独的SQLite数据库。 用来执行SQLite的命令。
2.FMResultSet – 表示FMDatabase执行查询后结果集,这个和Android的Cursor类似。
3.FMDatabaseQueue – 如果你想在多线程中执行多个查询或更新,你应该使用该类。这是线程安全的。

数据库创建
创建FMDatabase对象时参数为SQLite数据库文件路径。该路径可以是以下三种之一:
1..文件路径。该文件路径无需真实存,如果不存在会自动创建。
2..空字符串(@”")。表示会在临时目录创建一个空的数据库,当FMDatabase 链接关闭时,文件也被删除。
3.NULL. 将创建一个内在数据库。同样的,当FMDatabase连接关闭时,数据会被销毁。


直接上手例子,边走边说:
这次分享前期准备了一个model

Person.h 文件中

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h> @interface Person : NSObject @property(nonatomic, copy)NSString *name; @property(nonatomic, copy)NSString * gender; @property(nonatomic)NSInteger age,ID;//基本数据类型可以省略内存修饰符assign,因为默认使用的就是assign @property(nonatomic,strong)UIImage *photo; //自定义初始化方法 -(id)initWithName : (NSString *)name gender : (NSString *)gender age : (NSInteger)age photo : (UIImage *)photo; @end

Person.m文件

#import "Person.h" @implementation Person//自定义初始化方法 -(id)initWithName : (NSString *)name gender : (NSString *)gender age : (NSInteger)age photo : (UIImage *)photo{ if (self = [super init]) { self.name = name; self.gender = gender; self.age = age; self.photo = photo; } return self; } @end

打开数据库

在和数据库交互 之前,数据库必须是打开的。如果资源或权限不足无法打开或创建数据库,都会导致打开失败。

`-(void)viewDidLoad {
[super viewDidLoad];
// 获取Documents文件夹路径
NSString *urlString = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) lastObject];
// 在Documents文件夹里创建数据库文件 db.sqlite
NSString *dbPath = [urlString stringByAppendingPathComponent:@"db.sqlite"];
// 创建数据库对象
// 参数:数据库的路径

    // 执行结束后并没有帮我们生成数据库文件,只是帮我们创建了数据库对象    
 FMDatabase *db = [FMDatabase databaseWithPath:dbPath];     
 NSLog(@"%@",NSHomeDirectory());    
   // 代开数据库    
   // open操作才帮我们真正的创建数据库文件,且如果已经打开,直接返回YES,此时可以直接使用,如果打开失败会打印报错信息    
  BOOL isOpen = [db open];    
  if (isOpen) {        
       NSLog(@"打开成功");        
       //创建表格        
       //executeUpdate 除了查询之外,其他数据创建表格,插入数据、删除数据都是用这个方法       
      // blob 二进制流 相当于oc的NSData        
      BOOL isCreat =   [db executeUpdate:@"create table if not exists Person(id integer primary key autoincrement,name text,gender text,age integer,photo blob)"];        
      NSLog(@"%@",isCreat ? @"建表成功":@"建表失败");           
   }else{       
    NSLog(@"打开失败");    
   }    
 //给属性赋值
  self.db = db;
  NSLog(@"%@",NSHomeDirectory());
       // 可以根据打印的地址,自行查看

}`

执行更新
一切不是SELECT命令的命令都视为更新。这包括 CREATE, UPDATE, INSERT,ALTER,COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE (等)。
简单来说,只要不是以SELECT开头的命令都是UPDATE命令。

执行更新返回一个BOOL值。YES表示执行成功,否则表示有那些错误 。你可以调用 -lastErrorMessage 和 -lastErrorCode方法来得到更多信息。

// 插入
- (IBAction)insertButtonAction:(id)sender { Person *p = [[Person alloc]initWithName:@"美美" gender:@"女" age:20 photo:[UIImage imageNamed:@"3.gif"]]; //将图片转化成NSData对象 NSData *data = UIImagePNGRepresentation(p.photo); //@(p.age)参数必须是对象类型的才能使用 //插入操作 BOOL isInsert = [self.db executeUpdate:@"insert into Person(name,gender,age,photo)values(?,?,?,?)",p.name,p.gender,@(p.age),data]; NSLog(@"%@",isInsert ? @"插入成功":@"插入失败"); if (isInsert == YES) { NSLog(@"插入成功"); }else{ NSLog(@"插入失败"); } }


// 删除
- (IBAction)deleteButtonAction:(id)sender { //根据条件删除 BOOL result = [self.db executeUpdate:@"delete from Person where id = ?",@1]; if (result == YES) { NSLog(@"删除成功"); }else{ NSLog(@"删除失败"); } //删除全部表格内容 // id 不会清空 // BOOL isResult1 = [self.db executeUpdate:@"delete from Person"]; // 删除表格 // id 清空 // BOOL isResult2 = [self.db executeUpdate:@"drop table Person"]; }


// 更新
`- (IBAction)updataButtonActton:(id)sender {

    BOOL isUpdate = [self.db executeUpdate:@"update Person set gender = ? where id = ?",@"男",@4];    
    if (isUpdate == YES) {        
          NSLog(@"更新成功");    
    }else{        
          NSLog(@"更新入失败");    
   }   

}`

执行查询

SELECT命令就是查询,执行查询的方法是以 -excuteQuery开头的。

执行查询时,如果成功返回FMResultSet对象, 错误返回nil. 与执行更新相当,支持使用 NSError**参数。同时,你也可以使用 -lastErrorCode和-lastErrorMessage获知错误信息。

// 查询
- (IBAction)selectButtonActton:(id)sender { // 查询全部 FMResultSet *set = [self.db executeQuery:@"select * from Person"]; // 按条件查询 // FMResultSet *set = [self.db executeQuery:@"select *from Person where name = ?",@"美美"]; self.dataArray = [NSMutableArray arrayWithCapacity:0]; // 循环取出表中的数据 // [set next] 判断写一行是否有数据 // [set next]可以轮询query回来的资料,每一次的next可以得到一个row里对应的数值,并用[rs stringForColumn:]或[rs intForColumn:]等方法把值转成Object-C的型态。 // if [FMResultSet next] 查询某一条信息 while ([set next]) { //取出每一个字段对应的数据 NSInteger ID = [set intForColumn:@"id"]; //取出id字段下的数据 NSString *name = [set stringForColumn:@"name"];//取出name字段下的数据 NSString *gender = [set stringForColumn:@"gender"];//取出gender字段下的数据 NSInteger age = [set intForColumn:@"age"];//取出age字段下的数据 NSData *data = [set dataForColumn:@"photo"];//取出photo字段下的数据 //创建model类 //将二进制流转成图片 UIImage *image = [UIImage imageWithData:data]; Person *p = [[Person alloc]initWithName:name gender:gender age:age photo: image]; p.ID = ID; // 保存你需要的信息 [self.dataArray addObject:p]; } }

FMResultSet 提供了很多方法来获得所需的格式的值:
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnIndex:
objectForColumn:

这些方法也都包括 {type}ForColumnIndex 的这样子的方法,参数是查询结果集的列的索引位置。
你无需调用 [FMResultSet close]来关闭结果集, 当新的结果集产生,或者其数据库关闭时,会自动关闭。

关闭数据库
当使用完数据库,你应该 -close 来关闭数据库连接来释放SQLite使用的资源。

[db close]; 

事务

FMDatabase是支持事务的。

数据净化(数据格式化)

使用FMDB,插入数据前,你不要花时间审查你的数据。你可以使用标准的SQLite数据绑定语法。
INSERT INTO myTable VALUES (?, ?, ?)
SQLite会识别 “?” 为一个输入的点位符, 这样的执行会接受一个可变参数(或者表示为其他参数,如NSArray, NSDictionary,或va_list等),会正确为您转义。

你也可以选择使用命名参数语法。

 INSERT INTO myTable VALUES (:id, :name, :value)   

参数名必须以冒名开头。SQLite本身支持其他字符,当Dictionary key的内部实现是冒号开头。注意你的NSDictionary key不要包含冒号。

NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:@"My Name", @"name", nil];  
 [db executeUpdate:@"INSERT INTO myTable (name) VALUES (:name)" withParameterDictionary:argsDict];   

而且,代码不能这么写(为什么?想想吧。)

      [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @"this has \\" lots of ' bizarre \\" quotes '"]; 

你应该:

     [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @"this has " lots of ' bizarre " quotes '"];   

提供给 -executeUpdate: 方法的参数都必须是对象。就像以下的代码就无法工作,且会产生崩溃。

     [db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];   

正确有做法是把数字打包成 NSNumber对象

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];

或者,你可以使用 -execute*WithFormat: ,这是NSString风格的参数

    [db executeUpdateWithFormat:@"INSERT INTO myTable VALUES (%d)", 42]; 

-execute*WithFormat: 的方法的内部实现会帮你封装数据, 以下这些修饰符都可以使用: %@, %c, %s, %d, %D,%i, %u, %U, %hi, %hu, %qi, %qu, %f, %g, %ld, %lu, %lld, and %llu. 除此之外的修饰符可能导致无法预知的结果。 一些情况下,你需要在SQL语句中使用 % 字符,你应该使用 %%。

线程安全
如果我们的 app 需要多线程操作数据库,那么就需要使用 FMDatabaseQueue 来保证线程安全了。 切记不能在多个线程中共同一个 FMDatabase 对象并且在多个线程中同时使用,这个类本身不是线程安全的,这样使用会造成数据混乱等问题。

使用 FMDatabaseQueue 很简单,首先用一个数据库文件地址来初使化 FMDatabaseQueue,然后就可以将一个闭包 (block) 传入 inDatabase 方法中。 在闭包中操作数据库,而不直接参与 FMDatabase 的管理。

// 创建,最好放在一个单例的类中

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; // 使用[queue inDatabase:^(FMDatabase *db) { [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; FMResultSet *rs = [db executeQuery:@"select * from foo"]; while ([rs next]) { // … } }]; // 如果要支持事务 [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; if (whoopsSomethingWrongHappened) { *rollback = YES; return; } // etc… [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; }];

本文后期的事务和多线程没有什么想法就什么也没有添加。
本文章大部分都是别人的,我是为了做个笔记,也多个东西给大家参考下
转自:
唐巧超级大神引用
FMDB官方使用文档-GCD的使用-提高性能(翻译)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容