LKDBHelper.m 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720
  1. //
  2. // LKDBHelper.m
  3. // LJH
  4. //
  5. // Created by LJH on 12-12-6.
  6. // Copyright (c) 2012年 LJH. All rights reserved.
  7. //
  8. #import "LKDBHelper.h"
  9. #import <sqlite3.h>
  10. #ifndef SQLITE_OPEN_FILEPROTECTION_NONE
  11. #define SQLITE_OPEN_FILEPROTECTION_NONE 0x00400000
  12. #endif
  13. #define LKDBOpenFlags (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_PRIVATECACHE | SQLITE_OPEN_FILEPROTECTION_NONE)
  14. #define LKDBCheck_tableNameIsInvalid(tableName) \
  15. if ([LKDBUtils checkStringIsEmpty:tableName]) { \
  16. LKErrorLog(@" \n Fail!Fail!Fail!Fail! \n with TableName is nil"); \
  17. return NO; \
  18. }
  19. #define LKDBCode_Async_Begin \
  20. __weak LKDBHelper *wself = self; \
  21. [self asyncBlock :^{__strong LKDBHelper *sself = wself; \
  22. if (sself) {
  23. #define LKDBCode_Async_End \
  24. } \
  25. }];
  26. #define LKDBCheck_modelIsInvalid(model) \
  27. if (model == nil) { \
  28. LKErrorLog(@"model is nil"); \
  29. return NO; \
  30. } \
  31. if ([model.class getModelInfos].count == 0) { \
  32. LKErrorLog(@"class: %@ property count is 0!!", NSStringFromClass(model.class)); \
  33. return NO; \
  34. } \
  35. NSString *_model_tableName = model.db_tableName ?: [model.class getTableName]; \
  36. if ([LKDBUtils checkStringIsEmpty:_model_tableName]) { \
  37. LKErrorLog(@"model class name %@ table name is invalid!", NSStringFromClass(model.class)); \
  38. return NO; \
  39. }
  40. @interface NSObject (LKTabelStructure_Private)
  41. - (void)setDb_inserting:(BOOL)db_inserting;
  42. @end
  43. @interface LKDBWeakObject : NSObject
  44. @property (nonatomic, weak) LKDBHelper *obj;
  45. @end
  46. @interface LKDBHelper ()
  47. @property (nonatomic, weak) FMDatabase *usingdb;
  48. @property (nonatomic, strong) FMDatabaseQueue *bindingQueue;
  49. @property (nonatomic, copy) NSString *dbPath;
  50. @property (nonatomic, strong) NSMutableArray *createdTableNames;
  51. @property (nonatomic, strong) NSRecursiveLock *threadLock;
  52. @property (nonatomic, assign) NSInteger lastExecuteDBTime;
  53. @property (nonatomic, assign) BOOL runingAutoActionsTimer;
  54. @property (nonatomic, assign) NSInteger autoCloseDBDelayTime;
  55. @property (nonatomic, assign) BOOL inAutoReleasePool;
  56. @end
  57. @implementation LKDBHelper
  58. @synthesize encryptionKey = _encryptionKey;
  59. static BOOL LKDBLogErrorEnable = NO;
  60. + (void)setLogError:(BOOL)logError {
  61. if (LKDBLogErrorEnable == logError) {
  62. return;
  63. }
  64. #ifdef DEBUG
  65. LKDBLogErrorEnable = logError;
  66. NSMutableArray *dbArray = [self dbHelperSingleArray];
  67. @synchronized(dbArray) {
  68. [dbArray enumerateObjectsUsingBlock:^(LKDBWeakObject *weakObj, NSUInteger idx, BOOL *stop) {
  69. [weakObj.obj executeDB:^(FMDatabase *db) {
  70. db.logsErrors = LKDBLogErrorEnable;
  71. }];
  72. }];
  73. }
  74. #endif
  75. }
  76. static BOOL LKDBNullIsEmptyString = NO;
  77. + (void)setNullToEmpty:(BOOL)empty {
  78. LKDBNullIsEmptyString = empty;
  79. }
  80. + (BOOL)nullIsEmpty {
  81. return LKDBNullIsEmptyString;
  82. }
  83. + (NSMutableArray *)dbHelperSingleArray {
  84. static NSMutableArray *dbArray;
  85. static dispatch_once_t onceToken;
  86. dispatch_once(&onceToken, ^{
  87. dbArray = [NSMutableArray array];
  88. });
  89. return dbArray;
  90. }
  91. + (LKDBHelper *)dbHelperWithPath:(NSString *)dbFilePath save:(LKDBHelper *)helper {
  92. LKDBHelper *instance = nil;
  93. dbFilePath = dbFilePath.lowercaseString;
  94. NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
  95. BOOL hasCached = NO;
  96. NSMutableArray *dbArray = [self dbHelperSingleArray];
  97. @synchronized(dbArray) {
  98. for (NSInteger i = 0; i < dbArray.count; i++) {
  99. LKDBWeakObject *weakObj = [dbArray objectAtIndex:i];
  100. if ([weakObj.obj.dbPath.lowercaseString isEqualToString:dbFilePath]) {
  101. if (helper) {
  102. hasCached = YES;
  103. } else {
  104. instance = weakObj.obj;
  105. }
  106. } else if (!weakObj.obj) {
  107. [indexSet addIndex:i];
  108. }
  109. }
  110. [dbArray removeObjectsAtIndexes:indexSet];
  111. if (!hasCached && helper) {
  112. LKDBWeakObject *weakObj = [[LKDBWeakObject alloc] init];
  113. weakObj.obj = helper;
  114. [dbArray addObject:weakObj];
  115. }
  116. }
  117. return instance;
  118. }
  119. - (instancetype)init {
  120. return [self initWithDBName:@"LKDB"];
  121. }
  122. - (instancetype)initWithDBName:(NSString *)dbname {
  123. return [self initWithDBPath:[LKDBHelper getDBPathWithDBName:dbname]];
  124. }
  125. - (instancetype)initWithDBPath:(NSString *)filePath {
  126. if ([LKDBUtils checkStringIsEmpty:filePath]) {
  127. ///release self
  128. self = nil;
  129. return nil;
  130. }
  131. @synchronized([LKDBHelper class]) {
  132. LKDBHelper *helper = [LKDBHelper dbHelperWithPath:filePath save:nil];
  133. if (helper) {
  134. self = helper;
  135. } else {
  136. self = [super init];
  137. if (self) {
  138. self.threadLock = [[NSRecursiveLock alloc] init];
  139. self.createdTableNames = [NSMutableArray array];
  140. self.lastExecuteDBTime = CFAbsoluteTimeGetCurrent();
  141. self.autoCloseDBDelayTime = 15;
  142. self.enableAutoVacuum = YES;
  143. [self setDBPath:filePath];
  144. [LKDBHelper dbHelperWithPath:nil save:self];
  145. }
  146. }
  147. }
  148. return self;
  149. }
  150. #pragma mark - init FMDB
  151. + (NSString *)getDBPathWithDBName:(NSString *)dbName {
  152. NSString *fileName = nil;
  153. if ([dbName hasSuffix:@".db"] == NO) {
  154. fileName = [NSString stringWithFormat:@"%@.db", dbName];
  155. } else {
  156. fileName = dbName;
  157. }
  158. NSString *filePath = [LKDBUtils getPathForDocuments:fileName inDir:@"db"];
  159. return filePath;
  160. }
  161. - (void)setDBName:(NSString *)dbName {
  162. [self setDBPath:[LKDBHelper getDBPathWithDBName:dbName]];
  163. }
  164. - (void)setDBPath:(NSString *)filePath {
  165. [self.threadLock lock];
  166. if (self.bindingQueue && [self.dbPath isEqualToString:filePath]) {
  167. LKErrorLog(@"current dbPath isEqual filePath :%@", filePath);
  168. } else {
  169. // reset encryptionKey
  170. _encryptionKey = nil;
  171. // set db path
  172. self.dbPath = filePath;
  173. [self openDB];
  174. }
  175. [self.threadLock unlock];
  176. }
  177. - (void)openDB {
  178. /// 重置所有配置
  179. [self.bindingQueue close];
  180. [self.createdTableNames removeAllObjects];
  181. NSString *filePath = self.dbPath;
  182. BOOL hasCreated = [LKDBUtils createDirectoryWithFilePath:filePath];
  183. if (!hasCreated) {
  184. /// 数据库目录创建失败
  185. return;
  186. }
  187. self.bindingQueue = [[FMDatabaseQueue alloc] initWithPath:filePath
  188. flags:LKDBOpenFlags];
  189. [self.bindingQueue inDatabase:^(FMDatabase *db) {
  190. db.logsErrors = LKDBLogErrorEnable;
  191. }];
  192. #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
  193. NSFileManager *fileManager = [NSFileManager defaultManager];
  194. if ([fileManager fileExistsAtPath:filePath]) {
  195. [fileManager setAttributes:@{NSFileProtectionKey: NSFileProtectionNone} ofItemAtPath:filePath error:nil];
  196. }
  197. #endif
  198. }
  199. - (void)closeDB {
  200. [self.threadLock lock];
  201. [self.bindingQueue close];
  202. self.bindingQueue = nil;
  203. [self.threadLock unlock];
  204. }
  205. #pragma mark - core
  206. - (void)executeDB:(void (^)(FMDatabase *db))block {
  207. if (!block) {
  208. NSAssert(NO, @"block is nil!");
  209. return;
  210. }
  211. [self.threadLock lock];
  212. if (self.usingdb != nil) {
  213. block(self.usingdb);
  214. } else {
  215. if (self.bindingQueue == nil) {
  216. [self openDB];
  217. if (_encryptionKey.length > 0) {
  218. [self.bindingQueue inDatabase:^(FMDatabase *db) {
  219. [db setKey:_encryptionKey];
  220. }];
  221. }
  222. }
  223. [self.bindingQueue inDatabase:^(FMDatabase *db) {
  224. self.usingdb = db;
  225. block(db);
  226. self.usingdb = nil;
  227. }];
  228. }
  229. self.lastExecuteDBTime = CFAbsoluteTimeGetCurrent();
  230. // 执行定时器任务
  231. [self startAutoActionsTimer];
  232. [self.threadLock unlock];
  233. }
  234. - (void)setAutoCloseDBTime:(NSInteger)time {
  235. if (time < 0) {
  236. time = 0;
  237. }
  238. self.autoCloseDBDelayTime = time;
  239. // 执行定时器任务
  240. [self startAutoActionsTimer];
  241. }
  242. - (void)startAutoActionsTimer {
  243. // 无需执行任务
  244. if (!self.autoCloseDBDelayTime && !self.enableAutoVacuum) {
  245. return;
  246. }
  247. if (self.runingAutoActionsTimer) {
  248. return;
  249. }
  250. self.runingAutoActionsTimer = YES;
  251. __weak LKDBHelper *wself = self;
  252. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
  253. __strong LKDBHelper *self = wself;
  254. [self.threadLock lock];
  255. [self runAutoVacuumAction];
  256. [self runAutoCloseDBConnection];
  257. self.runingAutoActionsTimer = NO;
  258. if (self.bindingQueue != nil) {
  259. // 数据库链接未关闭,则继续执行定时器
  260. [self startAutoActionsTimer];
  261. }
  262. [self.threadLock unlock];
  263. });
  264. }
  265. - (void)runAutoCloseDBConnection {
  266. // 数据库链接已关闭
  267. if (!self.bindingQueue) {
  268. return;
  269. }
  270. // 未开启自动关闭数据库连接
  271. if (!self.autoCloseDBDelayTime) {
  272. return;
  273. }
  274. // 判断阈值内是否有操作
  275. const NSInteger nowTime = CFAbsoluteTimeGetCurrent();
  276. if (nowTime - self.lastExecuteDBTime < self.autoCloseDBDelayTime) {
  277. return;
  278. }
  279. // 关闭数据库链接
  280. [self closeDB];
  281. }
  282. - (void)runAutoVacuumAction {
  283. // 数据库链接已关闭
  284. if (!self.bindingQueue) {
  285. return;
  286. }
  287. // 未开启自动压缩
  288. if (!self.enableAutoVacuum) {
  289. return;
  290. }
  291. // 判断阈值内是否有操作
  292. const NSInteger nowTime = CFAbsoluteTimeGetCurrent();
  293. if (nowTime - self.lastExecuteDBTime < 10) {
  294. return;
  295. }
  296. // 读取全局缓存文件
  297. static NSMutableDictionary *dbAutoVaccumMap = nil;
  298. static NSString *dbAutoVaccumPath = nil;
  299. static dispatch_semaphore_t dbLock = NULL;
  300. static dispatch_once_t onceToken;
  301. dispatch_once(&onceToken, ^{
  302. NSString *cacheDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
  303. dbAutoVaccumPath = [cacheDirectory stringByAppendingString:@"lkdb-auto-vacuum.plist"];
  304. dbAutoVaccumMap = [NSMutableDictionary dictionaryWithContentsOfFile:dbAutoVaccumPath];
  305. if (!dbAutoVaccumMap) {
  306. dbAutoVaccumMap = [NSMutableDictionary dictionary];
  307. }
  308. dbLock = dispatch_semaphore_create(1);
  309. });
  310. // 3天操作一次
  311. NSString *dbKey = self.dbPath.lastPathComponent;
  312. dispatch_semaphore_wait(dbLock, DISPATCH_TIME_FOREVER);
  313. NSInteger lastTime = [[dbAutoVaccumMap objectForKey:dbKey] integerValue];
  314. if (0 == lastTime) {
  315. // 记录第一次运行的时间
  316. lastTime = nowTime;
  317. [dbAutoVaccumMap setObject:@(nowTime) forKey:dbKey];
  318. [dbAutoVaccumMap writeToFile:dbAutoVaccumPath atomically:YES];
  319. }
  320. dispatch_semaphore_signal(dbLock);
  321. if (nowTime - lastTime < 259200) { // 60 * 60 * 24 * 3
  322. return;
  323. }
  324. // 执行数据压缩
  325. [self executeSQL:@"vacuum" arguments:nil];
  326. // 记录执行时间
  327. dispatch_semaphore_wait(dbLock, DISPATCH_TIME_FOREVER);
  328. [dbAutoVaccumMap setObject:@(nowTime) forKey:dbKey];
  329. [dbAutoVaccumMap writeToFile:dbAutoVaccumPath atomically:YES];
  330. dispatch_semaphore_signal(dbLock);
  331. }
  332. - (BOOL)executeSQL:(NSString *)sql arguments:(NSArray *)args {
  333. __block BOOL execute = NO;
  334. [self executeDB:^(FMDatabase *db) {
  335. if (args.count > 0) {
  336. execute = [db executeUpdate:sql withArgumentsInArray:args];
  337. } else {
  338. execute = [db executeUpdate:sql];
  339. }
  340. if (db.hadError) {
  341. LKErrorLog(@" sql:%@ \n args:%@ \n sqlite error :%@ \n", sql, args, db.lastErrorMessage);
  342. }
  343. }];
  344. return execute;
  345. }
  346. - (NSString *)executeScalarWithSQL:(NSString *)sql arguments:(NSArray *)args {
  347. __block NSString *scalar = nil;
  348. [self executeDB:^(FMDatabase *db) {
  349. FMResultSet *set = nil;
  350. if (args.count > 0) {
  351. set = [db executeQuery:sql withArgumentsInArray:args];
  352. } else {
  353. set = [db executeQuery:sql];
  354. }
  355. if (db.hadError) {
  356. LKErrorLog(@" sql:%@ \n args:%@ \n sqlite error :%@ \n", sql, args, db.lastErrorMessage);
  357. }
  358. if (([set columnCount] > 0) && [set next]) {
  359. scalar = [set stringForColumnIndex:0];
  360. }
  361. [set close];
  362. }];
  363. return scalar;
  364. }
  365. - (void)executeForTransaction:(BOOL (^)(LKDBHelper *))block {
  366. LKDBHelper *helper = self;
  367. [self executeDB:^(FMDatabase *db) {
  368. BOOL inTransacttion = db.isInTransaction;
  369. if (!inTransacttion) {
  370. [db beginTransaction];
  371. }
  372. BOOL isCommit = NO;
  373. if (block) {
  374. isCommit = block(helper);
  375. }
  376. if (!inTransacttion) {
  377. if (isCommit) {
  378. [db commit];
  379. } else {
  380. [db rollback];
  381. }
  382. }
  383. }];
  384. }
  385. // splice 'where' 拼接where语句
  386. - (NSMutableArray *)extractQuery:(NSMutableString *)query where:(id)where {
  387. NSMutableArray *values = nil;
  388. if ([where isKindOfClass:[NSString class]] && ([LKDBUtils checkStringIsEmpty:where] == NO)) {
  389. [query appendFormat:@" where %@", where];
  390. } else if ([where isKindOfClass:[NSDictionary class]]) {
  391. NSDictionary *dicWhere = where;
  392. if (dicWhere.count > 0) {
  393. values = [NSMutableArray arrayWithCapacity:dicWhere.count];
  394. NSString *wherekey = [self dictionaryToSqlWhere:where andValues:values];
  395. [query appendFormat:@" where %@", wherekey];
  396. }
  397. }
  398. return values;
  399. }
  400. // dic where parse
  401. - (NSString *)dictionaryToSqlWhere:(NSDictionary *)dic andValues:(NSMutableArray *)values {
  402. if (dic.count == 0) {
  403. return @"";
  404. }
  405. NSMutableString *wherekey = [NSMutableString stringWithCapacity:0];
  406. [dic enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
  407. if ([obj isKindOfClass:[NSArray class]]) {
  408. NSArray *vlist = obj;
  409. if (vlist.count == 0) {
  410. return;
  411. }
  412. if (wherekey.length > 0) {
  413. [wherekey appendString:@" and"];
  414. }
  415. [wherekey appendFormat:@" %@ in(", key];
  416. [vlist enumerateObjectsUsingBlock:^(id vlist_obj, NSUInteger idx, BOOL *stop) {
  417. if (idx > 0) {
  418. [wherekey appendString:@","];
  419. }
  420. [wherekey appendString:@"?"];
  421. [values addObject:vlist_obj];
  422. }];
  423. [wherekey appendString:@")"];
  424. } else {
  425. if (wherekey.length > 0) {
  426. [wherekey appendFormat:@" and %@=?", key];
  427. } else {
  428. [wherekey appendFormat:@" %@=?", key];
  429. }
  430. [values addObject:obj];
  431. }
  432. }];
  433. return [wherekey copy];
  434. }
  435. // where sql statements about model primary keys
  436. - (NSMutableString *)primaryKeyWhereSQLWithModel:(NSObject *)model addPValues:(NSMutableArray *)addPValues {
  437. LKModelInfos *infos = [model.class getModelInfos];
  438. NSArray *primaryKeys = infos.primaryKeys;
  439. NSMutableString *pwhere = [NSMutableString string];
  440. if (primaryKeys.count > 0) {
  441. for (NSInteger i = 0; i < primaryKeys.count; i++) {
  442. NSString *pk = [primaryKeys objectAtIndex:i];
  443. if ([LKDBUtils checkStringIsEmpty:pk] == NO) {
  444. LKDBProperty *property = [infos objectWithSqlColumnName:pk];
  445. id pvalue = nil;
  446. if (property && [property.type isEqualToString:LKSQL_Mapping_UserCalculate]) {
  447. pvalue = [model userGetValueForModel:property];
  448. } else if (pk && property) {
  449. pvalue = [model modelGetValue:property];
  450. }
  451. if (pvalue) {
  452. if (pwhere.length > 0) {
  453. [pwhere appendString:@"and"];
  454. }
  455. if (addPValues) {
  456. [pwhere appendFormat:@" %@=? ", pk];
  457. [addPValues addObject:pvalue];
  458. } else {
  459. [pwhere appendFormat:@" %@='%@' ", pk, pvalue];
  460. }
  461. }
  462. }
  463. }
  464. }
  465. return pwhere;
  466. }
  467. #pragma mark - set key
  468. - (BOOL)setKey:(NSString *)key {
  469. [self.threadLock lock];
  470. _encryptionKey = [key copy];
  471. __block BOOL success = NO;
  472. if (self.bindingQueue && _encryptionKey.length > 0) {
  473. [self executeDB:^(FMDatabase *db) {
  474. success = [db setKey:self->_encryptionKey];
  475. }];
  476. }
  477. [self.threadLock unlock];
  478. return success;
  479. }
  480. - (BOOL)rekey:(NSString *)key {
  481. [self.threadLock lock];
  482. _encryptionKey = [key copy];
  483. __block BOOL success = NO;
  484. if (self.bindingQueue && _encryptionKey.length > 0) {
  485. [self executeDB:^(FMDatabase *db) {
  486. success = [db rekey:self->_encryptionKey];
  487. }];
  488. }
  489. [self.threadLock unlock];
  490. return success;
  491. }
  492. - (NSString *)encryptionKey {
  493. [self.threadLock lock];
  494. NSString *key = _encryptionKey;
  495. [self.threadLock unlock];
  496. return key;
  497. }
  498. #pragma mark - dealloc
  499. - (void)dealloc {
  500. NSMutableArray *dbArray = [LKDBHelper dbHelperSingleArray];
  501. @synchronized(dbArray) {
  502. NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
  503. for (NSInteger i = 0; i < dbArray.count; i++) {
  504. LKDBWeakObject *weakObj = [dbArray objectAtIndex:i];
  505. if (weakObj.obj == self) {
  506. weakObj.obj = nil;
  507. [indexSet addIndex:i];
  508. }
  509. }
  510. [dbArray removeObjectsAtIndexes:indexSet];
  511. }
  512. [self.bindingQueue close];
  513. self.usingdb = nil;
  514. self.bindingQueue = nil;
  515. self.dbPath = nil;
  516. self.threadLock = nil;
  517. }
  518. @end
  519. @implementation LKDBHelper (DatabaseManager)
  520. - (void)dropAllTable {
  521. [self executeDB:^(FMDatabase *db) {
  522. FMResultSet *set = [db executeQuery:@"select name from sqlite_master where type='table'"];
  523. NSMutableArray *dropTables = [NSMutableArray arrayWithCapacity:0];
  524. while ([set next]) {
  525. NSString *tableName = [set stringForColumnIndex:0];
  526. if (tableName) {
  527. [dropTables addObject:tableName];
  528. }
  529. }
  530. [set close];
  531. for (NSString *tableName in dropTables) {
  532. if ([tableName hasPrefix:@"sqlite_"] == NO) {
  533. NSString *dropTable = [NSString stringWithFormat:@"drop table %@", tableName];
  534. [db executeUpdate:dropTable];
  535. }
  536. }
  537. [self.createdTableNames removeAllObjects];
  538. }];
  539. }
  540. - (BOOL)dropTableWithClass:(Class)modelClass {
  541. return [self dropTableWithTableName:[modelClass getTableName]];
  542. }
  543. - (BOOL)dropTableWithTableName:(NSString *)tableName {
  544. LKDBCheck_tableNameIsInvalid(tableName);
  545. // 检测是否创建过表
  546. if ([self getTableCreatedWithTableName:tableName] == NO) {
  547. return YES;
  548. }
  549. NSString *dropTable = [NSString stringWithFormat:@"drop table %@", tableName];
  550. BOOL isDrop = [self executeSQL:dropTable arguments:nil];
  551. [self.threadLock lock];
  552. [self.createdTableNames removeObject:tableName];
  553. [self.threadLock unlock];
  554. return isDrop;
  555. }
  556. - (void)fixSqlColumnsWithClass:(Class)clazz tableName:(NSString *)tableName {
  557. [self executeDB:^(FMDatabase *db) {
  558. LKModelInfos *infos = [clazz getModelInfos];
  559. NSString *select = [NSString stringWithFormat:@"select * from %@ limit 0", tableName];
  560. FMResultSet *set = [db executeQuery:select];
  561. NSArray *columnArray = set.columnNameToIndexMap.allKeys;
  562. [set close];
  563. NSMutableArray *alterAddColumns = [NSMutableArray array];
  564. for (NSInteger i = 0; i < infos.count; i++) {
  565. LKDBProperty *property = [infos objectWithIndex:i];
  566. if ([property.sqlColumnName.lowercaseString isEqualToString:@"rowid"]) {
  567. continue;
  568. }
  569. ///数据库中不存在 需要alter add
  570. if ([columnArray containsObject:property.sqlColumnName.lowercaseString] == NO) {
  571. NSMutableString *addColumePars = [NSMutableString stringWithFormat:@"%@ %@", property.sqlColumnName, property.sqlColumnType];
  572. [clazz columnAttributeWithProperty:property];
  573. if ((property.length > 0) && [property.sqlColumnType isEqualToString:LKSQL_Type_Text]) {
  574. [addColumePars appendFormat:@"(%ld)", (long)property.length];
  575. }
  576. if (property.isNotNull) {
  577. [addColumePars appendFormat:@" %@", LKSQL_Attribute_NotNull];
  578. }
  579. if (property.checkValue) {
  580. [addColumePars appendFormat:@" %@(%@)", LKSQL_Attribute_Check, property.checkValue];
  581. }
  582. if (property.defaultValue) {
  583. [addColumePars appendFormat:@" %@ %@", LKSQL_Attribute_Default, property.defaultValue];
  584. }
  585. NSString *alertSQL = [NSString stringWithFormat:@"alter table %@ add column %@", tableName, addColumePars];
  586. NSString *defaultValue = property.defaultValue ?: @"0";
  587. if ([property.sqlColumnType isEqualToString:LKSQL_Type_Text]) {
  588. if (LKDBNullIsEmptyString) {
  589. defaultValue = @"''";
  590. } else {
  591. defaultValue = @"null";
  592. }
  593. }
  594. NSString *initColumnValue = [NSString stringWithFormat:@"update %@ set %@=%@", tableName, property.sqlColumnName, defaultValue];
  595. BOOL success = [db executeUpdate:alertSQL];
  596. if (success) {
  597. [db executeUpdate:initColumnValue];
  598. [alterAddColumns addObject:property];
  599. }
  600. }
  601. }
  602. if (alterAddColumns.count > 0) {
  603. [clazz dbDidAlterTable:self tableName:tableName addColumns:alterAddColumns];
  604. }
  605. }];
  606. }
  607. - (BOOL)_createTableWithModelClass:(Class)modelClass tableName:(NSString *)tableName {
  608. if (!tableName.length) {
  609. NSAssert(NO, @"none table name");
  610. return NO;
  611. }
  612. if ([self getTableCreatedWithTableName:tableName]) {
  613. // 已创建表 就跳过
  614. [self.threadLock lock];
  615. if ([self.createdTableNames containsObject:tableName] == NO) {
  616. [self.createdTableNames addObject:tableName];
  617. }
  618. [self.threadLock unlock];
  619. [self fixSqlColumnsWithClass:modelClass tableName:tableName];
  620. return YES;
  621. }
  622. LKModelInfos *infos = [modelClass getModelInfos];
  623. if (infos.count == 0) {
  624. LKErrorLog(@"Class: %@ 0属性 不需要创建表", NSStringFromClass(modelClass));
  625. return NO;
  626. }
  627. NSArray *primaryKeys = infos.primaryKeys;
  628. NSString *rowidAliasName = [modelClass db_rowidAliasName];
  629. NSMutableString *table_pars = [NSMutableString string];
  630. for (NSInteger i = 0; i < infos.count; i++) {
  631. if (i > 0) {
  632. [table_pars appendString:@","];
  633. }
  634. LKDBProperty *property = [infos objectWithIndex:i];
  635. [modelClass columnAttributeWithProperty:property];
  636. NSString *columnType = property.sqlColumnType;
  637. [table_pars appendFormat:@"%@ %@", property.sqlColumnName, columnType];
  638. if ([property.sqlColumnType isEqualToString:LKSQL_Type_Text]) {
  639. if (property.length > 0) {
  640. [table_pars appendFormat:@"(%ld)", (long)property.length];
  641. }
  642. }
  643. if (property.isNotNull) {
  644. [table_pars appendFormat:@" %@", LKSQL_Attribute_NotNull];
  645. }
  646. if (property.isUnique) {
  647. [table_pars appendFormat:@" %@", LKSQL_Attribute_Unique];
  648. }
  649. if (property.checkValue) {
  650. [table_pars appendFormat:@" %@(%@)", LKSQL_Attribute_Check, property.checkValue];
  651. }
  652. if (property.defaultValue) {
  653. [table_pars appendFormat:@" %@ %@", LKSQL_Attribute_Default, property.defaultValue];
  654. }
  655. if (rowidAliasName.length > 0) {
  656. if ([property.sqlColumnName isEqualToString:rowidAliasName]) {
  657. [table_pars appendString:@" primary key autoincrement"];
  658. }
  659. }
  660. }
  661. NSMutableString *pksb = [NSMutableString string];
  662. ///联合主键
  663. if (rowidAliasName.length == 0) {
  664. if (primaryKeys.count > 0) {
  665. pksb = [NSMutableString string];
  666. for (NSInteger i = 0; i < primaryKeys.count; i++) {
  667. NSString *pk = [primaryKeys objectAtIndex:i];
  668. if (pksb.length > 0) {
  669. [pksb appendString:@","];
  670. }
  671. [pksb appendString:pk];
  672. }
  673. if (pksb.length > 0) {
  674. [pksb insertString:@",primary key(" atIndex:0];
  675. [pksb appendString:@")"];
  676. }
  677. }
  678. }
  679. NSString *createTableSQL = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@%@)", tableName, table_pars, pksb];
  680. BOOL isCreated = [self executeSQL:createTableSQL arguments:nil];
  681. [self.threadLock lock];
  682. if (isCreated) {
  683. [self.createdTableNames addObject:tableName];
  684. [modelClass dbDidCreateTable:self tableName:tableName];
  685. }
  686. [self.threadLock unlock];
  687. return isCreated;
  688. }
  689. - (BOOL)getTableCreatedWithClass:(Class)modelClass {
  690. return [self getTableCreatedWithTableName:[modelClass getTableName]];
  691. }
  692. - (BOOL)getTableCreatedWithTableName:(NSString *)tableName {
  693. __block BOOL isTableCreated = NO;
  694. [self executeDB:^(FMDatabase *db) {
  695. FMResultSet *set = [db executeQuery:@"select count(name) from sqlite_master where type='table' and name=?", tableName];
  696. if ([set next]) {
  697. if ([set intForColumnIndex:0] > 0) {
  698. isTableCreated = YES;
  699. }
  700. }
  701. [set close];
  702. }];
  703. return isTableCreated;
  704. }
  705. @end
  706. @implementation LKDBHelper (DatabaseExecute)
  707. - (id)modelValueWithProperty:(LKDBProperty *)property model:(NSObject *)model {
  708. id value = nil;
  709. if (property.isUserCalculate) {
  710. value = [model userGetValueForModel:property];
  711. } else {
  712. value = [model modelGetValue:property];
  713. }
  714. if (value == nil) {
  715. if (LKDBNullIsEmptyString) {
  716. value = @"";
  717. } else {
  718. value = [NSNull null];
  719. }
  720. }
  721. return value;
  722. }
  723. - (void)asyncBlock:(void (^)(void))block {
  724. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
  725. }
  726. #pragma mark - row count operation
  727. - (NSInteger)rowCount:(Class)modelClass where:(id)where {
  728. return [self _rowCountWithTableName:nil where:where modelClass:modelClass];
  729. }
  730. - (void)rowCount:(Class)modelClass where:(id)where callback:(void (^)(NSInteger))callback {
  731. if (!callback) {
  732. return;
  733. }
  734. LKDBCode_Async_Begin;
  735. NSInteger result = [sself _rowCountWithTableName:nil where:where modelClass:modelClass];
  736. callback(result);
  737. LKDBCode_Async_End;
  738. }
  739. - (NSInteger)rowCountWithTableName:(NSString *)tableName where:(id)where {
  740. return [self _rowCountWithTableName:tableName where:where modelClass:nil];
  741. }
  742. - (NSInteger)_rowCountWithTableName:(NSString *)tableName where:(id)where modelClass:(Class)modelClass {
  743. if (!tableName) {
  744. tableName = [modelClass getTableName];
  745. }
  746. LKDBCheck_tableNameIsInvalid(tableName);
  747. if (modelClass) {
  748. // 检测是否创建过表
  749. [self.threadLock lock];
  750. if ([self.createdTableNames containsObject:tableName] == NO) {
  751. [self _createTableWithModelClass:modelClass tableName:tableName];
  752. }
  753. [self.threadLock unlock];
  754. }
  755. NSMutableString *rowCountSql = [NSMutableString stringWithFormat:@"select count(rowid) from %@", tableName];
  756. NSMutableArray *valuesarray = [self extractQuery:rowCountSql where:where];
  757. NSInteger result = [[self executeScalarWithSQL:rowCountSql arguments:valuesarray] integerValue];
  758. return result;
  759. }
  760. #pragma mark - search operation
  761. - (NSMutableArray *)search:(Class)modelClass where:(id)where orderBy:(NSString *)orderBy offset:(NSInteger)offset count:(NSInteger)count {
  762. return [self searchBase:modelClass columns:nil where:where orderBy:orderBy offset:offset count:count];
  763. }
  764. - (NSMutableArray *)search:(Class)modelClass column:(id)columns where:(id)where orderBy:(NSString *)orderBy offset:(NSInteger)offset count:(NSInteger)count {
  765. return [self searchBase:modelClass columns:columns where:where orderBy:orderBy offset:offset count:count];
  766. }
  767. - (id)searchSingle:(Class)modelClass where:(id)where orderBy:(NSString *)orderBy {
  768. NSMutableArray *array = [self searchBase:modelClass columns:nil where:where orderBy:orderBy offset:0 count:1];
  769. if (array.count > 0) {
  770. return [array objectAtIndex:0];
  771. }
  772. return nil;
  773. }
  774. - (void)search:(Class)modelClass where:(id)where orderBy:(NSString *)orderBy offset:(NSInteger)offset count:(NSInteger)count callback:(void (^)(NSMutableArray *))block {
  775. if (!block) {
  776. return;
  777. }
  778. LKDBCode_Async_Begin;
  779. LKDBQueryParams *params = [[LKDBQueryParams alloc] init];
  780. params.toClass = modelClass;
  781. if ([where isKindOfClass:[NSDictionary class]]) {
  782. params.whereDic = where;
  783. } else if ([where isKindOfClass:[NSString class]]) {
  784. params.where = where;
  785. }
  786. params.orderBy = orderBy;
  787. params.offset = offset;
  788. params.count = count;
  789. NSMutableArray *array = [sself searchBaseWithParams:params];
  790. block(array);
  791. LKDBCode_Async_End;
  792. }
  793. - (NSMutableArray *)searchBaseWithParams:(LKDBQueryParams *)params {
  794. if (params.toClass == nil) {
  795. LKErrorLog(@"you search pars:%@! \n toClass is nil", params.getAllPropertysString);
  796. return nil;
  797. }
  798. NSString *db_tableName = params.tableName;
  799. if ([LKDBUtils checkStringIsEmpty:db_tableName]) {
  800. db_tableName = [params.toClass getTableName];
  801. }
  802. if ([LKDBUtils checkStringIsEmpty:db_tableName]) {
  803. LKErrorLog(@"you search pars:%@! \n tableName is empty", params.getAllPropertysString);
  804. return nil;
  805. }
  806. // 检测是否创建过表
  807. [self.threadLock lock];
  808. if ([self.createdTableNames containsObject:db_tableName] == NO) {
  809. [self _createTableWithModelClass:params.toClass tableName:db_tableName];
  810. }
  811. [self.threadLock unlock];
  812. NSString *columnsString = nil;
  813. NSUInteger columnCount = 0;
  814. if (params.columnArray.count > 0) {
  815. columnCount = params.columnArray.count;
  816. columnsString = [params.columnArray componentsJoinedByString:@","];
  817. } else if ([LKDBUtils checkStringIsEmpty:params.columns] == NO) {
  818. columnsString = params.columns;
  819. NSArray *array = [params.columns componentsSeparatedByString:@","];
  820. columnCount = array.count;
  821. } else {
  822. columnsString = @"*";
  823. }
  824. NSMutableString *query = [NSMutableString stringWithFormat:@"select %@,rowid from %@", columnsString, db_tableName];
  825. NSMutableArray *whereValues = nil;
  826. if (params.whereDic.count > 0) {
  827. whereValues = [NSMutableArray arrayWithCapacity:params.whereDic.count];
  828. NSString *wherekey = [self dictionaryToSqlWhere:params.whereDic andValues:whereValues];
  829. [query appendFormat:@" where %@", wherekey];
  830. } else if ([LKDBUtils checkStringIsEmpty:params.where] == NO) {
  831. [query appendFormat:@" where %@", params.where];
  832. }
  833. [self sqlString:query groupBy:params.groupBy orderBy:params.orderBy offset:params.offset count:params.count];
  834. NSString * const executeQuery = query.copy;
  835. __block NSMutableArray *results = nil;
  836. [self executeDB:^(FMDatabase *db) {
  837. FMResultSet *set = nil;
  838. // 根据是否有 where 参数来决定调用哪个API
  839. if (whereValues.count == 0) {
  840. set = [db executeQuery:executeQuery];
  841. } else {
  842. set = [db executeQuery:executeQuery withArgumentsInArray:whereValues];
  843. }
  844. // Results to Models
  845. if (columnCount == 1) {
  846. results = [self executeOneColumnResult:set];
  847. } else {
  848. results = [self executeResult:set Class:params.toClass tableName:db_tableName];
  849. }
  850. // free sql handler
  851. [set close];
  852. }];
  853. return results;
  854. }
  855. - (NSMutableArray *)searchWithParams:(LKDBQueryParams *)params {
  856. if (params.callback) {
  857. LKDBCode_Async_Begin;
  858. NSMutableArray *array = [sself searchBaseWithParams:params];
  859. params.callback(array);
  860. LKDBCode_Async_End;
  861. return nil;
  862. } else {
  863. return [self searchBaseWithParams:params];
  864. }
  865. }
  866. - (NSMutableArray *)searchBase:(Class)modelClass columns:(id)columns where:(id)where orderBy:(NSString *)orderBy offset:(NSInteger)offset count:(NSInteger)count {
  867. LKDBQueryParams *params = [[LKDBQueryParams alloc] init];
  868. params.toClass = modelClass;
  869. if ([columns isKindOfClass:[NSArray class]]) {
  870. params.columnArray = columns;
  871. } else if ([columns isKindOfClass:[NSString class]]) {
  872. params.columns = columns;
  873. }
  874. if ([where isKindOfClass:[NSDictionary class]]) {
  875. params.whereDic = where;
  876. } else if ([where isKindOfClass:[NSString class]]) {
  877. params.where = where;
  878. }
  879. params.orderBy = orderBy;
  880. params.offset = offset;
  881. params.count = count;
  882. return [self searchBaseWithParams:params];
  883. }
  884. - (NSString *)replaceTableNameIfNeeded:(NSString *)sql withModelClass:(Class)modelClass {
  885. // 如果是单表查询情况下,给 query 追加 rowid column
  886. if ([sql componentsSeparatedByString:@" from "].count == 2 && [sql rangeOfString:@" join "].length == 0) {
  887. sql = [sql stringByReplacingOccurrencesOfString:@" from " withString:@",rowid from "];
  888. }
  889. // 无需替换 tableName
  890. if (!modelClass || [sql rangeOfString:@"@t"].length == 0) {
  891. return sql;
  892. }
  893. NSString * const tableName = [modelClass getTableName];
  894. if (!tableName) {
  895. return sql;
  896. }
  897. // 检测是否创建过表
  898. [self.threadLock lock];
  899. if ([self.createdTableNames containsObject:tableName] == NO) {
  900. [self _createTableWithModelClass:modelClass tableName:tableName];
  901. }
  902. [self.threadLock unlock];
  903. // replace @t to model table name
  904. if ([sql hasSuffix:@" @t"]) {
  905. sql = [sql stringByAppendingString:@" "];
  906. }
  907. sql = [sql stringByReplacingOccurrencesOfString:@" @t "
  908. withString:[NSString stringWithFormat:@" %@ ", tableName]];
  909. sql = [sql stringByReplacingOccurrencesOfString:@" @t,"
  910. withString:[NSString stringWithFormat:@" %@,", tableName]];
  911. sql = [sql stringByReplacingOccurrencesOfString:@",@t "
  912. withString:[NSString stringWithFormat:@",%@ ", tableName]];
  913. return sql;
  914. }
  915. - (NSMutableArray *)searchWithSQL:(NSString *)sql toClass:(Class)modelClass {
  916. sql = [self replaceTableNameIfNeeded:sql withModelClass:modelClass];
  917. return [self searchWithRAWSQL:sql toClass:modelClass];
  918. }
  919. - (NSMutableArray *)searchWithRAWSQL:(NSString *)sql toClass:(Class)modelClass {
  920. __block NSMutableArray *results = nil;
  921. [self executeDB:^(FMDatabase *db) {
  922. FMResultSet *set = [db executeQuery:sql];
  923. results = [self executeResult:set Class:modelClass tableName:nil];
  924. [set close];
  925. }];
  926. return results;
  927. }
  928. - (NSMutableArray *)search:(Class)modelClass withSQL:(NSString *)sql, ... {
  929. va_list args;
  930. va_start(args, sql);
  931. sql = [self replaceTableNameIfNeeded:sql withModelClass:modelClass];
  932. va_list *argsPoint = &args;
  933. __block NSMutableArray *results = nil;
  934. [self executeDB:^(FMDatabase *db) {
  935. FMResultSet *set = [db executeQuery:sql withVAList:*argsPoint];
  936. results = [self executeResult:set Class:modelClass tableName:nil];
  937. [set close];
  938. }];
  939. va_end(args);
  940. return results;
  941. }
  942. - (void)sqlString:(NSMutableString *)sql groupBy:(NSString *)groupBy orderBy:(NSString *)orderby offset:(NSInteger)offset count:(NSInteger)count {
  943. if ([LKDBUtils checkStringIsEmpty:groupBy] == NO) {
  944. [sql appendFormat:@" group by %@", groupBy];
  945. }
  946. if ([LKDBUtils checkStringIsEmpty:orderby] == NO) {
  947. [sql appendFormat:@" order by %@", orderby];
  948. }
  949. if (count > 0) {
  950. [sql appendFormat:@" limit %ld offset %ld", (long)count, (long)offset];
  951. } else if (offset > 0) {
  952. [sql appendFormat:@" limit %d offset %ld", INT_MAX, (long)offset];
  953. }
  954. }
  955. - (NSMutableArray *)executeOneColumnResult:(FMResultSet *)set {
  956. NSMutableArray *array = [NSMutableArray arrayWithCapacity:0];
  957. while ([set next]) {
  958. NSString *string = [set stringForColumnIndex:0];
  959. if (string) {
  960. [array addObject:string];
  961. } else {
  962. NSData *data = [set dataForColumnIndex:0];
  963. if (data) {
  964. [array addObject:data];
  965. }
  966. }
  967. }
  968. return array;
  969. }
  970. - (void)inAutoReleaseExecuteBlock:(void(^)(void))block {
  971. if (self.inAutoReleasePool) {
  972. // 已在 @autoreleasepool 范围内
  973. block();
  974. } else {
  975. @autoreleasepool {
  976. self.inAutoReleasePool = YES;
  977. block();
  978. self.inAutoReleasePool = NO;
  979. }
  980. }
  981. }
  982. - (void)foreachResultSet:(FMResultSet *)set block:(void(^)(void))block {
  983. while ([set next]) {
  984. [self inAutoReleaseExecuteBlock:block];
  985. }
  986. }
  987. - (NSMutableArray *)executeResult:(FMResultSet *)set Class:(Class)modelClass tableName:(NSString *)tableName {
  988. NSMutableArray *array = [NSMutableArray arrayWithCapacity:0];
  989. if (!modelClass) {
  990. // 防止内存释放太慢引起的 OOM,用 autorelease 包一层
  991. [self foreachResultSet:set block:^{
  992. NSDictionary *dict = [set resultDictionary];
  993. if (dict) {
  994. [array addObject:dict];
  995. }
  996. }];
  997. } else {
  998. LKModelInfos *infos = [modelClass getModelInfos];
  999. NSInteger columnCount = [set columnCount];
  1000. ///当主键是int类型时 会替换掉rowid
  1001. NSString *rowidAliasName = [modelClass db_rowidAliasName];
  1002. // 防止内存释放太慢引起的 OOM,用 autorelease 包一层
  1003. [self foreachResultSet:set block:^{
  1004. NSObject *bindingModel = [[modelClass alloc] init];
  1005. if (bindingModel == nil) {
  1006. return;
  1007. }
  1008. for (int i = 0; i < columnCount; i++) {
  1009. NSString *sqlName = [set columnNameForIndex:i];
  1010. LKDBProperty *property = [infos objectWithSqlColumnName:sqlName];
  1011. BOOL isRowid = [[sqlName lowercaseString] isEqualToString:@"rowid"];
  1012. if ((isRowid == NO) && (property == nil)) {
  1013. continue;
  1014. }
  1015. if (isRowid && ((property == nil) || [property.sqlColumnType isEqualToString:LKSQL_Type_Int])) {
  1016. bindingModel.rowid = [set longForColumnIndex:i];
  1017. } else {
  1018. BOOL isUserCalculate = [property.type isEqualToString:LKSQL_Mapping_UserCalculate];
  1019. if (property.propertyName && (isUserCalculate == NO)) {
  1020. NSString *sqlValue = [set stringForColumnIndex:i];
  1021. [bindingModel modelSetValue:property value:sqlValue];
  1022. if ([rowidAliasName isEqualToString:sqlName]) {
  1023. bindingModel.rowid = [set longForColumnIndex:i];
  1024. }
  1025. } else {
  1026. NSData *sqlData = [set dataForColumnIndex:i];
  1027. NSString *sqlValue = nil;
  1028. if (sqlData) {
  1029. sqlValue = [[NSString alloc] initWithData:sqlData encoding:NSUTF8StringEncoding];
  1030. }
  1031. [bindingModel userSetValueForModel:property value:sqlValue ?: sqlData];
  1032. }
  1033. }
  1034. }
  1035. bindingModel.db_tableName = tableName;
  1036. [modelClass dbDidSeleted:bindingModel];
  1037. [array addObject:bindingModel];
  1038. }];
  1039. }
  1040. return array;
  1041. }
  1042. #pragma mark - insert operation
  1043. - (BOOL)insertToDB:(NSObject *)model {
  1044. BOOL success = [self insertBase:model];
  1045. return success;
  1046. }
  1047. - (void)insertToDB:(NSObject *)model callback:(void (^)(BOOL))block {
  1048. LKDBCode_Async_Begin;
  1049. BOOL success = [sself insertBase:model];
  1050. if (block) {
  1051. block(success);
  1052. }
  1053. LKDBCode_Async_End;
  1054. }
  1055. - (BOOL)insertWhenNotExists:(NSObject *)model {
  1056. if ([self isExistsModel:model] == NO) {
  1057. return [self insertToDB:model];
  1058. }
  1059. return NO;
  1060. }
  1061. - (void)insertWhenNotExists:(NSObject *)model callback:(void (^)(BOOL))block {
  1062. LKDBCode_Async_Begin;
  1063. BOOL result = [sself insertWhenNotExists:model];
  1064. if (block) {
  1065. block(result);
  1066. }
  1067. LKDBCode_Async_End;
  1068. }
  1069. - (BOOL)insertBase:(NSObject *)model {
  1070. LKDBCheck_modelIsInvalid(model);
  1071. Class modelClass = model.class;
  1072. // callback
  1073. if ([modelClass dbWillInsert:model] == NO) {
  1074. LKErrorLog(@"your cancel %@ insert", model);
  1075. return NO;
  1076. }
  1077. [model setDb_inserting:YES];
  1078. NSString *db_tableName = model.db_tableName ?: [modelClass getTableName];
  1079. // 检测是否创建过表
  1080. [self.threadLock lock];
  1081. if ([self.createdTableNames containsObject:db_tableName] == NO) {
  1082. [self _createTableWithModelClass:modelClass tableName:db_tableName];
  1083. }
  1084. [self.threadLock unlock];
  1085. // --
  1086. LKModelInfos *infos = [modelClass getModelInfos];
  1087. NSMutableString *insertKey = [NSMutableString stringWithCapacity:0];
  1088. NSMutableString *insertValuesString = [NSMutableString stringWithCapacity:0];
  1089. NSMutableArray *insertValues = [NSMutableArray arrayWithCapacity:infos.count];
  1090. LKDBProperty *primaryProperty = [model singlePrimaryKeyProperty];
  1091. for (NSInteger i = 0; i < infos.count; i++) {
  1092. LKDBProperty *property = [infos objectWithIndex:i];
  1093. if ([LKDBUtils checkStringIsEmpty:property.sqlColumnName]) {
  1094. continue;
  1095. }
  1096. if ([property isEqual:primaryProperty]) {
  1097. if ([property.sqlColumnType isEqualToString:LKSQL_Type_Int] && [model singlePrimaryKeyValueIsEmpty]) {
  1098. continue;
  1099. }
  1100. }
  1101. id value = [self modelValueWithProperty:property model:model];
  1102. if (value == nil) {
  1103. continue;
  1104. }
  1105. ///跳过 rowid = 0 的属性
  1106. if ([property.sqlColumnName isEqualToString:@"rowid"] && ([value intValue] == 0)) {
  1107. continue;
  1108. }
  1109. if (insertKey.length > 0) {
  1110. [insertKey appendString:@","];
  1111. [insertValuesString appendString:@","];
  1112. }
  1113. [insertKey appendString:property.sqlColumnName];
  1114. [insertValuesString appendString:@"?"];
  1115. [insertValues addObject:value];
  1116. }
  1117. // 拼接insertSQL 语句 采用 replace 插入
  1118. NSString *insertSQL = [NSString stringWithFormat:@"replace into %@(%@) values(%@)", db_tableName, insertKey, insertValuesString];
  1119. __block BOOL execute = NO;
  1120. __block sqlite_int64 lastInsertRowId = 0;
  1121. [self executeDB:^(FMDatabase *db) {
  1122. execute = [db executeUpdate:insertSQL withArgumentsInArray:insertValues];
  1123. lastInsertRowId = db.lastInsertRowId;
  1124. if (db.hadError) {
  1125. LKErrorLog(@" sql:%@ \n args:%@ \n sqlite error :%@ \n", insertSQL, insertValues, db.lastErrorMessage);
  1126. }
  1127. }];
  1128. model.rowid = (NSInteger)lastInsertRowId;
  1129. [model setDb_inserting:NO];
  1130. // callback
  1131. [modelClass dbDidInserted:model result:execute];
  1132. return execute;
  1133. }
  1134. #pragma mark - update operation
  1135. - (BOOL)updateToDB:(NSObject *)model where:(id)where {
  1136. BOOL success = [self updateToDBBase:model where:where];
  1137. return success;
  1138. }
  1139. - (void)updateToDB:(NSObject *)model where:(id)where callback:(void (^)(BOOL))block {
  1140. LKDBCode_Async_Begin;
  1141. BOOL success = [sself updateToDBBase:model where:where];
  1142. if (block) {
  1143. block(success);
  1144. }
  1145. LKDBCode_Async_End;
  1146. }
  1147. - (BOOL)updateToDBBase:(NSObject *)model where:(id)where {
  1148. LKDBCheck_modelIsInvalid(model);
  1149. Class modelClass = model.class;
  1150. // callback
  1151. if ([modelClass dbWillUpdate:model] == NO) {
  1152. LKErrorLog(@"you cancel %@ update.", model);
  1153. return NO;
  1154. }
  1155. NSString *db_tableName = model.db_tableName ?: [modelClass getTableName];
  1156. // 检测是否创建过表
  1157. [self.threadLock lock];
  1158. if ([self.createdTableNames containsObject:db_tableName] == NO) {
  1159. [self _createTableWithModelClass:modelClass tableName:db_tableName];
  1160. }
  1161. [self.threadLock unlock];
  1162. LKModelInfos *infos = [modelClass getModelInfos];
  1163. NSMutableString *updateKey = [NSMutableString string];
  1164. NSMutableArray *updateValues = [NSMutableArray arrayWithCapacity:infos.count];
  1165. for (NSInteger i = 0; i < infos.count; i++) {
  1166. LKDBProperty *property = [infos objectWithIndex:i];
  1167. if ([LKDBUtils checkStringIsEmpty:property.sqlColumnName]) {
  1168. continue;
  1169. }
  1170. id value = [self modelValueWithProperty:property model:model];
  1171. if (value == nil) {
  1172. continue;
  1173. }
  1174. ///跳过 rowid = 0 的属性
  1175. if ([property.sqlColumnName isEqualToString:@"rowid"]) {
  1176. int rowid = [value intValue];
  1177. if (rowid > 0) {
  1178. ///如果rowid 已经存在就不修改
  1179. NSString *rowidWhere = [NSString stringWithFormat:@"rowid=%d", rowid];
  1180. NSInteger rowCount = [self rowCountWithTableName:db_tableName where:rowidWhere];
  1181. if (rowCount > 0) {
  1182. continue;
  1183. }
  1184. } else {
  1185. continue;
  1186. }
  1187. }
  1188. if (updateKey.length > 0) {
  1189. [updateKey appendString:@","];
  1190. }
  1191. [updateKey appendFormat:@"%@=?", property.sqlColumnName];
  1192. [updateValues addObject:value];
  1193. }
  1194. NSMutableString *updateSQL = [NSMutableString stringWithFormat:@"update %@ set %@ where ", db_tableName, updateKey];
  1195. // 添加where 语句
  1196. if ([where isKindOfClass:[NSString class]] && ([LKDBUtils checkStringIsEmpty:where] == NO)) {
  1197. [updateSQL appendString:where];
  1198. } else if ([where isKindOfClass:[NSDictionary class]] && ([(NSDictionary *)where count] > 0)) {
  1199. NSMutableArray *valuearray = [NSMutableArray array];
  1200. NSString *sqlwhere = [self dictionaryToSqlWhere:where andValues:valuearray];
  1201. [updateSQL appendString:sqlwhere];
  1202. [updateValues addObjectsFromArray:valuearray];
  1203. } else if (model.rowid > 0) {
  1204. [updateSQL appendFormat:@" rowid=%ld", (long)model.rowid];
  1205. } else {
  1206. // 如果不通过 rowid 来 更新数据 那 primarykey 一定要有值
  1207. NSString *pwhere = [self primaryKeyWhereSQLWithModel:model addPValues:updateValues];
  1208. if (pwhere.length == 0) {
  1209. LKErrorLog(@"database update fail : %@ no find primary key!", NSStringFromClass(modelClass));
  1210. return NO;
  1211. }
  1212. [updateSQL appendString:pwhere];
  1213. }
  1214. BOOL execute = [self executeSQL:updateSQL arguments:updateValues];
  1215. // callback
  1216. [modelClass dbDidUpdated:model result:execute];
  1217. return execute;
  1218. }
  1219. - (BOOL)updateToDB:(Class)modelClass set:(NSString *)sets where:(id)where {
  1220. return [self _updateToDBWithTableName:nil set:sets where:where modelClass:modelClass];
  1221. }
  1222. - (BOOL)updateToDBWithTableName:(NSString *)tableName set:(NSString *)sets where:(id)where {
  1223. return [self _updateToDBWithTableName:tableName set:sets where:where modelClass:nil];
  1224. }
  1225. - (BOOL)_updateToDBWithTableName:(NSString *)tableName set:(NSString *)sets where:(id)where modelClass:(Class)modelClass {
  1226. if (!tableName) {
  1227. tableName = [modelClass getTableName];
  1228. }
  1229. LKDBCheck_tableNameIsInvalid(tableName);
  1230. if (modelClass) {
  1231. // 检测是否创建过表
  1232. [self.threadLock lock];
  1233. if ([self.createdTableNames containsObject:tableName] == NO) {
  1234. [self _createTableWithModelClass:modelClass tableName:tableName];
  1235. }
  1236. [self.threadLock unlock];
  1237. }
  1238. NSMutableString *updateSQL = [NSMutableString stringWithFormat:@"update %@ set %@ ", tableName, sets];
  1239. NSMutableArray *updateValues = [self extractQuery:updateSQL where:where];
  1240. BOOL execute = [self executeSQL:updateSQL arguments:updateValues];
  1241. return execute;
  1242. }
  1243. #pragma mark - delete operation
  1244. - (BOOL)deleteToDB:(NSObject *)model {
  1245. return [self deleteToDBBase:model];
  1246. }
  1247. - (void)deleteToDB:(NSObject *)model callback:(void (^)(BOOL))block {
  1248. LKDBCode_Async_Begin;
  1249. BOOL isDeleted = [sself deleteToDBBase:model];
  1250. if (block) {
  1251. block(isDeleted);
  1252. }
  1253. LKDBCode_Async_End;
  1254. }
  1255. - (BOOL)deleteToDBBase:(NSObject *)model {
  1256. LKDBCheck_modelIsInvalid(model);
  1257. Class modelClass = model.class;
  1258. // callback
  1259. if ([modelClass dbWillDelete:model] == NO) {
  1260. LKErrorLog(@"you cancel %@ delete", model);
  1261. return NO;
  1262. }
  1263. NSString *db_tableName = model.db_tableName ?: [modelClass getTableName];
  1264. // 检测是否创建过表
  1265. [self.threadLock lock];
  1266. if ([self.createdTableNames containsObject:db_tableName] == NO) {
  1267. [self _createTableWithModelClass:modelClass tableName:db_tableName];
  1268. }
  1269. [self.threadLock unlock];
  1270. NSMutableString *deleteSQL = [NSMutableString stringWithFormat:@"delete from %@ where ", db_tableName];
  1271. NSMutableArray *parsArray = [NSMutableArray array];
  1272. if (model.rowid > 0) {
  1273. [deleteSQL appendFormat:@"rowid = %ld", (long)model.rowid];
  1274. } else {
  1275. NSString *pwhere = [self primaryKeyWhereSQLWithModel:model addPValues:parsArray];
  1276. if (pwhere.length == 0) {
  1277. LKErrorLog(@"delete fail : %@ primary value is nil", NSStringFromClass(modelClass));
  1278. return NO;
  1279. }
  1280. [deleteSQL appendString:pwhere];
  1281. }
  1282. if (parsArray.count == 0) {
  1283. parsArray = nil;
  1284. }
  1285. BOOL execute = [self executeSQL:deleteSQL arguments:parsArray];
  1286. // callback
  1287. [modelClass dbDidDeleted:model result:execute];
  1288. return execute;
  1289. }
  1290. - (BOOL)deleteWithClass:(Class)modelClass where:(id)where {
  1291. return [self _deleteWithTableName:nil where:where modelClass:modelClass];
  1292. }
  1293. - (void)deleteWithClass:(Class)modelClass where:(id)where callback:(void (^)(BOOL))block {
  1294. LKDBCode_Async_Begin;
  1295. BOOL isDeleted = [sself _deleteWithTableName:nil where:where modelClass:modelClass];
  1296. if (block) {
  1297. block(isDeleted);
  1298. }
  1299. LKDBCode_Async_End;
  1300. }
  1301. - (BOOL)deleteWithTableName:(NSString *)tableName where:(id)where {
  1302. return [self _deleteWithTableName:tableName where:where modelClass:nil];
  1303. }
  1304. - (BOOL)_deleteWithTableName:(NSString *)tableName where:(id)where modelClass:(Class)modelClass {
  1305. if (!tableName) {
  1306. tableName = [modelClass getTableName];
  1307. }
  1308. LKDBCheck_tableNameIsInvalid(tableName);
  1309. if (modelClass) {
  1310. // 检测是否创建过表
  1311. [self.threadLock lock];
  1312. if ([self.createdTableNames containsObject:tableName] == NO) {
  1313. [self _createTableWithModelClass:modelClass tableName:tableName];
  1314. }
  1315. [self.threadLock unlock];
  1316. }
  1317. NSMutableString *deleteSQL = [NSMutableString stringWithFormat:@"delete from %@", tableName];
  1318. NSMutableArray *values = [self extractQuery:deleteSQL where:where];
  1319. BOOL result = [self executeSQL:deleteSQL arguments:values];
  1320. return result;
  1321. }
  1322. #pragma mark - other operation
  1323. - (BOOL)isExistsModel:(NSObject *)model {
  1324. LKDBCheck_modelIsInvalid(model);
  1325. NSString *pwhere = nil;
  1326. if (model.rowid > 0) {
  1327. pwhere = [NSString stringWithFormat:@"rowid=%ld", (long)model.rowid];
  1328. } else {
  1329. pwhere = [self primaryKeyWhereSQLWithModel:model addPValues:nil];
  1330. }
  1331. if (pwhere.length == 0) {
  1332. LKErrorLog(@"exists model fail: primary key is nil or invalid");
  1333. return NO;
  1334. }
  1335. return [self isExistsClass:model.class where:pwhere];
  1336. }
  1337. - (BOOL)isExistsClass:(Class)modelClass where:(id)where {
  1338. return [self isExistsWithTableName:[modelClass getTableName] where:where];
  1339. }
  1340. - (BOOL)isExistsWithTableName:(NSString *)tableName where:(id)where {
  1341. return [self rowCountWithTableName:tableName where:where] > 0;
  1342. }
  1343. #pragma mark - clear operation
  1344. + (void)clearTableData:(Class)modelClass {
  1345. [[modelClass getUsingLKDBHelper] deleteWithClass:modelClass where:nil];
  1346. }
  1347. + (void)clearNoneImage:(Class)modelClass columns:(NSArray *)columns {
  1348. [self clearFileWithTable:modelClass columns:columns type:1];
  1349. }
  1350. + (void)clearNoneData:(Class)modelClass columns:(NSArray *)columns {
  1351. [self clearFileWithTable:modelClass columns:columns type:2];
  1352. }
  1353. #define LKTestDirFilename @"LKTestDirFilename111"
  1354. + (void)clearFileWithTable:(Class)modelClass columns:(NSArray *)columns type:(NSInteger)type {
  1355. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  1356. NSString *testpath = nil;
  1357. switch (type) {
  1358. case 1: {
  1359. testpath = [modelClass getDBImagePathWithName:LKTestDirFilename];
  1360. } break;
  1361. case 2: {
  1362. testpath = [modelClass getDBDataPathWithName:LKTestDirFilename];
  1363. } break;
  1364. }
  1365. if ([LKDBUtils checkStringIsEmpty:testpath]) {
  1366. return;
  1367. }
  1368. NSString *dir = [testpath stringByReplacingOccurrencesOfString:LKTestDirFilename withString:@""];
  1369. NSUInteger count = columns.count;
  1370. // 获取该目录下所有文件名
  1371. NSArray *files = [LKDBUtils getFilenamesWithDir:dir];
  1372. NSString *seleteColumn = [columns componentsJoinedByString:@","];
  1373. NSMutableString *whereStr = [NSMutableString string];
  1374. for (NSInteger i = 0; i < count; i++) {
  1375. [whereStr appendFormat:@" %@ != '' ", [columns objectAtIndex:i]];
  1376. if (i < count - 1) {
  1377. [whereStr appendString:@" or "];
  1378. }
  1379. }
  1380. NSString *querySql = [NSString stringWithFormat:@"select %@ from %@ where %@", seleteColumn, [modelClass getTableName], whereStr];
  1381. __block NSArray *dbfiles;
  1382. [[modelClass getUsingLKDBHelper] executeDB:^(FMDatabase *db) {
  1383. NSMutableArray *tempfiles = [NSMutableArray arrayWithCapacity:6];
  1384. FMResultSet *set = [db executeQuery:querySql];
  1385. while ([set next]) {
  1386. for (int j = 0; j < count; j++) {
  1387. NSString *str = [set stringForColumnIndex:j];
  1388. if ([LKDBUtils checkStringIsEmpty:str] == NO) {
  1389. [tempfiles addObject:str];
  1390. }
  1391. }
  1392. }
  1393. [set close];
  1394. dbfiles = tempfiles;
  1395. }];
  1396. // 遍历 当不再数据库记录中 就删除
  1397. for (NSString *deletefile in files) {
  1398. if ([dbfiles indexOfObject:deletefile] == NSNotFound) {
  1399. [LKDBUtils deleteWithFilepath:[dir stringByAppendingPathComponent:deletefile]];
  1400. }
  1401. }
  1402. });
  1403. }
  1404. @end
  1405. @implementation LKDBHelper (Deprecated_Nonfunctional)
  1406. - (void)setEncryptionKey:(NSString *)encryptionKey {
  1407. [self setKey:encryptionKey];
  1408. }
  1409. + (LKDBHelper *)sharedDBHelper {
  1410. return [LKDBHelper getUsingLKDBHelper];
  1411. }
  1412. - (BOOL)createTableWithModelClass:(Class)modelClass {
  1413. return [self _createTableWithModelClass:modelClass tableName:[modelClass getTableName]];
  1414. }
  1415. + (LKDBHelper *)getUsingLKDBHelper {
  1416. return [[LKDBHelper alloc] init];
  1417. }
  1418. @end
  1419. @implementation LKDBWeakObject
  1420. @end