我在寫(xiě)UDUserDefaultsModel(文章鏈接,github)這個(gè)庫(kù)時(shí)曾經(jīng)立下一個(gè)flag:要寫(xiě)一個(gè)基于model來(lái)存取數(shù)據(jù)庫(kù)的庫(kù),最近剛離職,所以就整合了一下,希望大家多多支持。
在iOS開(kāi)發(fā)過(guò)程當(dāng)中,難免用到數(shù)據(jù)庫(kù),以FMDB居多。以下是一個(gè)根據(jù)年齡篩選數(shù)據(jù)的sql語(yǔ)句:
select * from Student where age > 10 and age < 20 or age > 30 order by age desc limit 20
這樣寫(xiě)其實(shí)沒(méi)什么問(wèn)題,但是在我個(gè)人看來(lái)難以接受,字符串看起來(lái)太別扭。比如我再添加一個(gè)條件,那么就需要修改整個(gè)字符串了。
如果可以很好的控制sql語(yǔ)句,將大大提高編程效率。為此,YIIFMDB就改善了這個(gè)缺陷:純面向?qū)ο?,直接操作Model,完全不需要寫(xiě)sql語(yǔ)句。
其靈感源自于php的Yii 2架構(gòu),因?yàn)槲以诳磒hp代碼當(dāng)中,我發(fā)現(xiàn)根本就看不到sql語(yǔ)句,而php程序猿也說(shuō):“我們的工作就是操作數(shù)據(jù)庫(kù),但是卻不寫(xiě)sql語(yǔ)句”。
以下是YIIFMDB詳細(xì)用法:
YIIFMDB有兩個(gè)類(lèi):YIIFMDB和YIIParameters。其中YIIFMDB封裝了數(shù)據(jù)庫(kù)相關(guān)的操作,比如增刪改查之類(lèi),而YIIParameters則封裝了where之后的參數(shù),比如上段代碼當(dāng)中的:
age > 10 and age < 20 or age > 30 order by age desc limit 20
就可以在YIIParameters當(dāng)中完成。
接下來(lái)逐一介紹YIIParameters類(lèi)和YIIFMDB類(lèi)的使用:
YIIParameters類(lèi)
sql語(yǔ)句當(dāng)中where之后的參數(shù)基本上由以下模塊構(gòu)成:
- and(與操作)
- or(或操作)
- order by(排序)
- limit(數(shù)量限制)
其中的and和or又要配置“>,<,=,>=,<=,!=,like”關(guān)系,order by又有“ase,dese”的排序操作。
YIIParameters這個(gè)類(lèi)就包含了以上所有元素。以上面的where之后的sql語(yǔ)句為例,具體用法如下:
// 初始化YIIParameters
YIIParameters *parameters = [[YIIParameters alloc] init];
// 執(zhí)行and操作,將age限制在10-20之間
// age > 10,YIIParametersRelationTypeGreaterThan標(biāo)志">"
[parameters andWhere:@"age" value:@"10" relationType:YIIParametersRelationTypeGreaterThan];
// age < 20,YIIParametersRelationTypeLessThan標(biāo)志"<"
[parameters andWhere:@"age" value:@"20" relationType:YIIParametersRelationTypeLessThan];
// 以上是and,也就是形成的sql語(yǔ)句為: age > 10 and age < 20
// 執(zhí)行or操作,將age限制在age > 30 以上
[parameters orWhere:@"age" value:@"30" relationType:YIIParametersRelationTypeGreaterThan];
// 根據(jù)age進(jìn)行降序排列
// YIIParametersOrderTypeDesc表示降序"desc",YIIParametersOrderTypeAsc
[parameters orderByColumn:@"age" orderType:YIIParametersOrderTypeDesc];
// 將數(shù)據(jù)的個(gè)數(shù)限制在20個(gè)
parameters.limitCount = 20;
配置完畢,驗(yàn)證其是否配置正確,那么可以調(diào)用一下方法就行了:
NSLog(@"where參數(shù)為:%@", parameters.whereParameters);
當(dāng)然,如果參數(shù)都沒(méi)法配置了,則可以設(shè)置whereParameters。而對(duì)于YIIParameters更詳細(xì)的解釋請(qǐng)參考 YIIFMDB中的YIIParameters.h。
YIIFMDB類(lèi)
YIIParameters用來(lái)配置sql語(yǔ)句當(dāng)中where之后的參數(shù),而YIIFMDB類(lèi)則是對(duì)數(shù)據(jù)庫(kù)操作的進(jìn)一步封裝,具體如下:
獲取YIIFMDB單例
YIIFMDB *db = [YIIFMDB shareDatabase]; // 推薦使用
// 或者
YIIFMDB *db = [YIIFMDB shareDatabaseForName:@"ABC.sqlite" path:path]; // 自定義數(shù)據(jù)庫(kù)名字和路徑,在第一次實(shí)例的時(shí)候傳入,以后使用上面方法即可。
主鍵的字段
@property (nonatomic, readonly, copy) NSString *primaryKey; // 返回"yii_pkID",我自己在創(chuàng)建數(shù)據(jù)庫(kù)是配置的主鍵字段
是否打印log
@property (nonatomic, assign) BOOL shouldOpenDebugLog; // 默認(rèn)為NO,設(shè)為YES,會(huì)在控制器后臺(tái)打印數(shù)據(jù)庫(kù)操作相關(guān)的一些信息
創(chuàng)建一張表
[[YIIFMDB shareDatabase] createTableWithModelClass:[LCPersonModel class] excludedProperties:nil tableName:@"Person"];
此方法是創(chuàng)建一張名為@"Person"表,并且,表里面的字段也就是LCPersonModel里面的屬性,字段的數(shù)據(jù)類(lèi)型也對(duì)應(yīng)LCPersonModel里面的數(shù)據(jù)類(lèi)型
插入一條數(shù)據(jù)(增)
LCPersonModel *model = [[LCPersonModel alloc] init];
model.name = [NSString stringWithFormat:@"lc%d", (arc4random() % 100)];
model.gender = arc4random() % 2;
model.age = arc4random() % 80;
model.floatNumber = (arc4random() % 20) / 100.0;
model.doubleNumber = (arc4random() % 20) / 100.0;
model.isMan = arc4random() % 2;
model.number = @(arc4random() % 10);
YIIFMDB *db = [YIIFMDB shareDatabase];
BOOL isSuccess = [db insertWithModel:model tableName:tableName]; //插入一條數(shù)據(jù)
[db insertWithModels:@[model] tableName:tableName]; // 批量插入數(shù)據(jù)
刪除數(shù)據(jù)(刪)
-(BOOL)deleteFromTable:(NSString * _Nonnull)tableName whereParameters:(YIIParameters *)parameters; // 根據(jù)參數(shù)刪除一條數(shù)據(jù),YIIParameters參考上面
-(BOOL)deleteAllDataFromTable:(NSString * _Nonnull)tableName; // 刪除表中的所有數(shù)據(jù)
YIIFMDB *db = [YIIFMDB shareDatabase];
YIIParameters *parameters = [[YIIParameters alloc] init];
// db.primaryKey 是數(shù)據(jù)庫(kù)的主鍵,這條語(yǔ)句意思是刪除主鍵 = 1的那條數(shù)據(jù)
[parameters andWhere:db.primaryKey value:@"1" relationType:YIIParametersRelationTypeEqualTo];
[db deleteFromTable:tableName whereParameters:parameters];
更改數(shù)據(jù)(改)
-(BOOL)updateTable:(NSString * _Nonnull)tableName dictionary:(NSDictionary * _Nonnull)dictionary whereParameters:(YIIParameters *)parameters; // 更新一條數(shù)據(jù)
YIIFMDB *db = [YIIFMDB shareDatabase];
YIIParameters *parameters = [[YIIParameters alloc] init];
// 參數(shù)設(shè)置為主鍵 = 10
[parameters andWhere:db.primaryKey value:@"10" relationType:YIIParametersRelationTypeEqualTo];
// 將主鍵為10的那條數(shù)據(jù)的name更改為monkey
[db updateTable:tableName dictionary:@{@"name": @"monkey"} whereParameters:parameters];
查詢(xún)數(shù)據(jù)
-(NSArray *)queryFromTable:(NSString * _Nonnull)tableName model:(Class _Nonnull)modelClass whereParameters:(YIIParameters *)parameters; // 根據(jù)YIIParameters條件從表為tableName的查詢(xún)數(shù)據(jù)
YIIFMDB *db = [YIIFMDB shareDatabase];
YIIParameters *parameters = [[YIIParameters alloc] init];
[parameters andWhere:db.primaryKey value:@"5" relationType:YIIParametersRelationTypeLessThan];
NSLog(@"主鍵小于5的數(shù)據(jù):%@", [db queryFromTable:tableName model:[LCPersonModel class] whereParameters:parameters]);
除了增刪改查之外,YIIFMDB還提供了增加一個(gè)屬性,刪除一張表,獲取表中所有字段名,獲取表中數(shù)據(jù)個(gè)數(shù),表是否存在,求和,求平均值,最大值,最小值等功能,詳情請(qǐng)參考YIIFMDB的文檔。
線程安全操作(隊(duì)列和事務(wù))
由于FMDB本身就是是不安全的,上面的方法也是不安全的,為了保證其安全則需要結(jié)合隊(duì)列和事務(wù)操作,參考FMDB的隊(duì)列和事務(wù)。
-(void)inDatabase:(dispatch_block_t)block; // 將數(shù)據(jù)庫(kù)相關(guān)操作寫(xiě)在block里可保證線程安全
YIIFMDB *db = [YIIFMDB shareDatabase];
[db inDatabase:^{
// 增刪改查放在此代碼塊里執(zhí)行則可以保證線程安全
}];
-(void)inTransaction:(void(^)(BOOL *rollback))block; // 在block里寫(xiě)入代碼可執(zhí)行回滾操作
YIIFMDB *db = [YIIFMDB shareDatabase];
[db inTransaction:^(BOOL *rollback) {
// 如果某一個(gè)操作失誤,則可以執(zhí)行回滾操作
BOOL isSuccess = YES; // 數(shù)據(jù)庫(kù)操作是否操作成功
if (!isSuccess) {
*rollback = YES; // 回滾操作
return ;
}
}];
這里還有兩個(gè)缺陷:
- 未支持聯(lián)表查詢(xún)
- 未支持Model當(dāng)中套Model的插入。
如果是聯(lián)表查詢(xún)的話,那么需要獲取到YIIFMDB單例的"currentDatabase"來(lái)實(shí)現(xiàn)聯(lián)表查詢(xún),而Model套Model的,則最好是創(chuàng)建兩張表。