为了大家和项目的考虑, 如果使用过程中出现问题或者需要新功能接口提供, 请提一个issue, 需求合理会马上更新, 我每天都会关注 !
- 针对于FMDB的二次封装
- 线程安全
- 支持事务操作(目前others都仅支持线程安全)
- 操作简单, Model和Dictionary直接存储
- 拓展性强
- 不侵入你的任何Model
- 不需要实现某些奇怪的协议
Cocoapods
1. 在 Podfile 中添加 `pod 'JQFMDB'`
2. 执行 `pod install` 或 `pod update`
3. 导入 <JQFMDB/JQFMDB.h>
手动安装
1. 下载 JQFMDB 文件夹内的所有内容。
2. 将 JQFMDB.h和JQFMDB.m 以及FMDB添加(拖放)到你的工程。
3. 导入 "JQFMDB.h"。
- + (instancetype)shareDatabase;
- + (instancetype)shareDatabase:(NSString *)dbName;
- + (instancetype)shareDatabase:(NSString *)dbName path:(NSString *)dbPath;
单例方法创建数据库, 如果使用shareDatabase创建,则默认在NSDocumentDirectory下创建JQFMDB.sqlite, 但只要使用这三个方法任意一个创建成功, 之后即可使用三个中任意一个方法获得同一个实例,参数可随意或nil
dbName
数据库的名称 如: @"Users.sqlite", 如果dbName = nil,则默认dbName=@"JQFMDB.sqlite
dbPath
数据库的路径, 如果dbPath = nil, 则路径默认NSDocumentDirectory
// 创建数据库
JQFMDB *db = [JQFMDB shareDatabase];
或
JQFMDB *db = [JQFMDB shareDatabase:@"test.sqlite"];
或
JQFMDB *db = [JQFMDB shareDatabase:@"test.sqlite" path:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]];
- - (instancetype)initWithDBName:(NSString *)dbName;
- - (instancetype)initWithDBName:(NSString *)dbName path:(NSString *)dbPath;
如果操作几个数据库可以init方法获得不同实例, 参数说明同上.
//Person类构成
@interface Person : NSObject
@property (nonatomic, strong)NSString *name;
@property (nonatomic, strong)NSNumber *phoneNum;
@property (nonatomic, strong)NSData *photoData;
@property (nonatomic, assign)NSInteger luckyNum;
@property (nonatomic, assign)BOOL sex;
@property (nonatomic, assign)int age;
@property (nonatomic, assign)float height; //float类型存入172.12会变成172.19995, 取值时%.2f等于原值172.12
@property (nonatomic, assign)double weight;
// 为了测试除以上类型外, 下面的类型不参与建表
@property (nonatomic, strong)NSDictionary *testDic;
@property (nonatomic, strong)NSArray *testArr;
@property (nonatomic, strong)NSError *testError;
@property (nonatomic, strong)Person *testP;
@end
// 创建表 @"user"=表的名称 表的字段为Person的有效属性
[db jq_createTable:@"user" dicOrModel:[Person class]];
// 创建表 @"user"=表的名称 表的字段为字典的key,类型为字典的value
[db jq_createTable:@"user" dicOrModel:@{@"name":@"TEXT", @"age":@"INTEGER"}];
主键是默认自动创建的,名为pkid,如果你需要在你的Model中使用主键, 需要添加主键属性, 属性名必须为pkid
@property (nonatomic, assign)NSInteger pkid;
主键不会参加插入和修改操作
无论你想插入的是一个model还是dictionary都没问题,都会智能接收并存储;插入一组数据, 也支持model和dictionary混合的数组
- - (BOOL)jq_insertTable:(NSString *)tableName dicOrModel:(id)parameters;
- - (NSArray *)jq_insertTable:(NSString *)tableName dicOrModelArray:(NSArray *)dicOrModelArray;
Person *person = [[Person alloc] init];
person.name = @"cleanmonkey";
person.phoneNum = @(18866668888);
person.photoData = UIImagePNGRepresentation([UIImage imageNamed:@"bg.jpg"]);
person.luckyNum = 7;
person.sex = 0;
person.age = 26;
person.height = 172.12;
person.weight = 120.4555;
// 向user表中插入一条数据
[db jq_insertTable:@"user" dicOrModel:person];
或者你也可以用字典插入部分数据
[db jq_insertTable:@"user" dicOrModel:@{@"name":@"cleanmonkey",@"phoneNum":@(18866668888)}];
NSMutableArray *mArr = [NSMutableArray arrayWithCapacity:0];
for (int i = 0; i < 3; i++) {
Person *person = [[Person alloc] init];
person.name = [self randomName];
person.phoneNum = @(18866668888);
person.photoData = UIImagePNGRepresentation([UIImage imageNamed:@"bg.jpg"]);
person.luckyNum = 7;
person.sex = arc4random()%2;
person.age = 26;
person.height = 172.12;
person.weight = 120.4555;
[mArr addObject:person];
}
// 向user表中插入一组数据
[db jq_insertTable:@"user" dicOrModelArray:mArr];
// 或者也可以是model和dic混合形式的数组
[db jq_insertTable:@"user" dicOrModelArray:@[person, @{@"name":@"cleanmonkey"}, person]]
//异步(防止UI卡死)插入一条数据, 也同样可以使用线程安全的方法(在jq_inDatabase的block中插入)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[db jq_insertTable:@"user" dicOrModel:person];
});
根据条件语句删除想要删除的数据;删除表中全部数据
- - (BOOL)jq_deleteTable:(NSString *)tableName whereFormat:(NSString *)format, ...;
- - (BOOL)jq_deleteAllDataFromTable:(NSString *)tableName;
//删除最后一条数据
[db jq_deleteTable:@"user" whereFormat:@"WHERE rowid = (SELECT max(rowid) FROM user)"];
//删除全部数据
[db jq_deleteAllDataFromTable:@"user"];
parameters为要更新的数据,可以是model或dictionary, format为条件语句
- - (BOOL)jq_updateTable:(NSString *)tableName dicOrModel:(id)parameters whereFormat:(NSString *)format, ...;
//更新最后一条数据 name=testName , dicOrModel的参数也可以是name为testName的person
[db jq_updateTable:@"user" dicOrModel:@{@"name":@"testName"} whereFormat:@"WHERE rowid = (SELECT max(rowid) FROM user)"];
//把表中所有的name改成godlike
[db jq_updateTable:@"user" dicOrModel:@{@"name":@"godlike"} whereFormat:nil];
parameters为查找到数据后每条数据要存入的模型,可以为model或dictionary
- - (NSArray *)jq_lookupTable:(NSString *)tableName dicOrModel:(id)parameters whereFormat:(NSString *)format, ...;
////查找name=cleanmonkey的数据
NSArray *personArr = [db jq_lookupTable:@"user" dicOrModel:[Person class] whereFormat:@"where name = 'cleanmonkey'"];
NSLog(@"name=cleanmonkey:%@", personArr);
//查找表中所有数据
NSArray *personArr = [db jq_lookupTable:@"user" dicOrModel:[Person class] whereFormat:nil];
NSLog(@"表中所有数据:%@", personArr);
以上操作是非线程安全的, 要想保证线程安全,还是采用FMDB的原型,所有操作都放在下面block中执行, 而block块内代码会被提交到一个队列中,从而保证线程安全, 但要注意的是block不能嵌套使用
/**
将操作语句放入block中即可保证线程安全, 如:
Person *p = [[Person alloc] init];
p.name = @"小李";
[jqdb jq_inDatabase:^{
[jqdb jq_insertTable:@"users" dicOrModel:p];
}];
*/
- (void)jq_inDatabase:(void (^)(void))block;
[db jq_inDatabase:^{
[db jq_insertTable:@"user" dicOrModel:@{@"name":@"cleanmonkey"}];
[db jq_lookupTable:@"user" dicOrModel:[Person class] whereFormat:@"where name = 'cleanmonkey'"];
}];
用A给B转账100元的问题来简单阐述下事务, 首先查询下A的余额,如果>=100元,那么A账户先减去100元, 接着查询B账户的余额, B账户加上100元, 如果说在这之间有任何一个环节出了问题(余额不够, A查询或减去100元操作失败, B查询或加上100元操作失败),则进行回滚操作,相当于回到操作之前的状态,简单说,这就是一个事务操作
/**
Person *p = [[Person alloc] init];
p.name = @"小李";
for (int i=0,i < 1000,i++) {
[jq jq_inTransaction:^(BOOL *rollback) {
BOOL flag = [jq jq_insertTable:@"users" dicOrModel:p];
if (!flag) {
*rollback = YES; //只要有一次不成功,则进行回滚操作
return;
}
}];
}
*/
// 操作事务的方法
- (void)jq_inTransaction:(void(^)(BOOL *rollback))block;
`
Demo(用法注释很详细)和JQFMDB都已经放在了我的GitHub上,更多功能会陆续更新 如果觉得有用,帮忙点个star,十分感谢!