上次實(shí)現(xiàn)FMDB的CURD基本操作后攻谁,用在項(xiàng)目里,每個(gè)實(shí)體類都要寫SQL語(yǔ)句來(lái)實(shí)現(xiàn)創(chuàng)建表和CURD操作,總覺(jué)得太麻煩,然后就想著利用反射和kvc來(lái)實(shí)現(xiàn)一個(gè)數(shù)據(jù)庫(kù)操作的基類繼承一下训措,子類只需要繼承,然后添加自己的屬性就好光羞,這里做一個(gè)總結(jié)绩鸣。
第一個(gè)難點(diǎn):獲取子類的所有屬性以及類型
OC中有提供獲取所有property的方法,需要用到objc_property_t和class_copyPropertyList纱兑。
objc_property_t?*properties =class_copyPropertyList([selfclass], &outCount); 數(shù)組properties有所有的屬性以及其相應(yīng)的屬性類型呀闻。具體的代碼如下:
/**
?*??獲取該類的所有屬性
?*/??
+?(NSDictionary?*)getPropertys??
{??
NSMutableArray?*proNames?=?[NSMutableArray?array];??
NSMutableArray?*proTypes?=?[NSMutableArray?array];??
NSArray?*theTransients?=?[[self?class]?transients];??
unsignedint?outCount,?i;??
objc_property_t?*properties?=?class_copyPropertyList([self?class],?&outCount);??
for?(i?=?0;?i?<?outCount;?i++)?{??
????????objc_property_t?property?=?properties[i];??
//獲取屬性名??
NSString?*propertyName?=?[NSString?stringWithCString:property_getName(property)?encoding:NSUTF8StringEncoding];??
if?([theTransients?containsObject:propertyName])?{??
continue;??
????????}??
[proNames?addObject:propertyName];??
//獲取屬性類型等參數(shù)??
NSString?*propertyType?=?[NSString?stringWithCString:?property_getAttributes(property)?encoding:NSUTF8StringEncoding];??
/*
?????????c?char?????????C?unsigned?char
?????????i?int??????????I?unsigned?int
?????????l?long?????????L?unsigned?long
?????????s?short????????S?unsigned?short
?????????d?double???????D?unsigned?double
?????????f?float????????F?unsigned?float
?????????q?long?long????Q?unsigned?long?long
?????????B?BOOL
?????????@?對(duì)象類型?//指針?對(duì)象類型?如NSString?是@“NSString”
?????????64位下long?和long?long?都是Tq
?????????SQLite?默認(rèn)支持五種數(shù)據(jù)類型TEXT、INTEGER潜慎、REAL捡多、BLOB、NULL
?????????*/??
if?([propertyType?hasPrefix:@"T@"])?{??
[proTypes?addObject:SQLTEXT];??
}else?if?([propertyType?hasPrefix:@"Ti"]||[propertyType?hasPrefix:@"TI"]||[propertyType?hasPrefix:@"Ts"]||[propertyType?hasPrefix:@"TS"]||[propertyType?hasPrefix:@"TB"])?{??
[proTypes?addObject:SQLINTEGER];??
}else?{??
[proTypes?addObject:SQLREAL];??
????????}??
????}??
????free(properties);??
return?[NSDictionary?dictionaryWithObjectsAndKeys:proNames,@"name",proTypes,@"type",nil];??
}??
propertyTpe 在Xcode document 中铐炫,截圖
類型主要是在創(chuàng)建表的時(shí)候需要用到垒手,SQLite只支持五種數(shù)據(jù)類型TEXT、INTEGER倒信、REAL科贬、BLOB、NULL鳖悠,這里就需要將獲取到得屬性的類型轉(zhuǎn)換一下榜掌,我主要用了TEXT/INTEGER/REAL三種。
然后就是拼接SQL語(yǔ)句了乘综,這個(gè)也比較簡(jiǎn)單憎账,創(chuàng)建表的SQL語(yǔ)句拼接在這里:
+?(NSString?*)getColumeAndTypeString??
{??
NSMutableString*?pars?=?[NSMutableString?string];??
NSDictionary?*dict?=?[self.class?getAllProperties];??
NSMutableArray?*proNames?=?[dict?objectForKey:@"name"];??
NSMutableArray?*proTypes?=?[dict?objectForKey:@"type"];??
for?(int?i=0;?i<?proNames.count;?i++)?{??
[pars?appendFormat:@"%@?%@",[proNames?objectAtIndex:i],[proTypes?objectAtIndex:i]];??
if(i+1?!=?proNames.count)??
????????{??
[pars?appendString:@","];??
????????}??
????}??
return?pars;??
}??
第二個(gè)難點(diǎn):查詢出來(lái)的數(shù)據(jù)賦值給對(duì)象的對(duì)應(yīng)屬性
這里用到了KVC來(lái)賦值,主要方法就是setValue:forKey:
依然是先判斷屬性的類型卡辰,然后轉(zhuǎn)換相應(yīng)的數(shù)據(jù)類型鼠哥,我這里主要是NSString和NSNumber熟菲。
if?([columeType?isEqualToString:SQLTEXT])?{??
[model?setValue:[resultSet?stringForColumn:columeName]?forKey:columeName];??
}else?{??
[model?setValue:[NSNumber?numberWithLongLong:[resultSet?longLongIntForColumn:columeName]]?forKey:columeName];??
????????????????}?
第三個(gè)難點(diǎn):子類在實(shí)例化時(shí),直接創(chuàng)建對(duì)應(yīng)的數(shù)據(jù)庫(kù)表朴恳,這里用dispatch_once實(shí)現(xiàn)抄罕,只需要?jiǎng)?chuàng)建一次。子類中覆寫createTable方法于颖。
+?(BOOL)createTable??
{??
__blockBOOL?result?=?YES;??
static?dispatch_once_t?onceToken;??
????dispatch_once(&onceToken,?^{??
result?=?[super?createTable];??
????});??
return?result;??
}?
因?yàn)闀?huì)有多線程操作呆贿,所以用單例和FMDatabaseQueue來(lái)執(zhí)行,要避免queue里調(diào)用queue執(zhí)行操作森渐。
有一些屬性不需要與數(shù)據(jù)庫(kù)表字段映射做入,覆寫方法+ (NSArray *)transients;在改方法中返回不需要映射的字段數(shù)組。
基類中的方法如下:
/**?
?*??獲取該類的所有屬性
?*/??
+?(NSDictionary?*)getPropertys;??
/**?獲取所有屬性同衣,包括主鍵?*/??
+?(NSDictionary?*)getAllProperties;??
/**?數(shù)據(jù)庫(kù)中是否存在表?*/??
+?(BOOL)isExistInTable;??
/**?保存或更新
?*?如果不存在主鍵竟块,保存,
?*?有主鍵耐齐,則更新
?*/??
-?(BOOL)saveOrUpdate;??
/**?保存單個(gè)數(shù)據(jù)?*/??
-?(BOOL)save;??
/**?批量保存數(shù)據(jù)?*/??
+?(BOOL)saveObjects:(NSArray?*)array;??
/**?更新單個(gè)數(shù)據(jù)?*/??
-?(BOOL)update;??
/**?批量更新數(shù)據(jù)*/??
+?(BOOL)updateObjects:(NSArray?*)array;??
/**?刪除單個(gè)數(shù)據(jù)?*/??
-?(BOOL)deleteObject;??
/**?批量刪除數(shù)據(jù)?*/??
+?(BOOL)deleteObjects:(NSArray?*)array;??
/**?通過(guò)條件刪除數(shù)據(jù)?*/??
+?(BOOL)deleteObjectsByCriteria:(NSString?*)criteria;??
/**?查詢?nèi)繑?shù)據(jù)?*/??
+?(NSArray?*)findAll;??
/**?通過(guò)主鍵查詢?*/??
+?(instancetype)findByPK:(int)inPk;??
/**?查找某條數(shù)據(jù)?*/??
+?(instancetype)findFirstByCriteria:(NSString?*)criteria;??
/**?通過(guò)條件查找數(shù)據(jù)?
?*?這樣可以進(jìn)行分頁(yè)查詢?@"?WHERE?pk?>?5?limit?10"
?*/??
+?(NSArray?*)findByCriteria:(NSString?*)criteria;??
#pragma?mark?-?must?be?override?method??
/**
?*?創(chuàng)建表
?*?如果已經(jīng)創(chuàng)建浪秘,返回YES
?*/??
+?(BOOL)createTable;??
/**?如果子類中有一些property不需要?jiǎng)?chuàng)建數(shù)據(jù)庫(kù)字段,那么這個(gè)方法必須在子類中重寫?
?*/??
+?(NSArray?*)transients;??
JKDBModel添加了自動(dòng)更新的檢測(cè)和添加實(shí)體類字段的功能埠况。最新代碼在Github耸携。
具體用法請(qǐng)轉(zhuǎn)到github下載源碼:https://github.com/Joker-King/JKDBModel