- 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的使用-提高性能(翻译)