// // BGDB.m // BGFMDB // // Created by biao on 2017/10/18. // Copyright © 2017年 Biao. All rights reserved. // #import "BGDB.h" #import "BGTool.h" #import "NSCache+BGCache.h" /** 默认数据库名称 */ #define SQLITE_NAME @"BGFMDB.db" #define bg_debug(param) do{\ if(self.debug){bg_log(@"调试输出: %@",param);}\ }while(0) #define MaxQueryPageNum 50 #define CachePath(name) [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:name] static const void * const BGFMDBDispatchQueueSpecificKey = &BGFMDBDispatchQueueSpecificKey; @interface BGDB() /** 数据库队列 */ @property (nonatomic, strong) FMDatabaseQueue *queue; @property (nonatomic, strong) FMDatabase* db; @property (nonatomic, assign) BOOL inTransaction; /** 多线程池 */ @property(nonatomic,strong)NSMutableArray* _Nullable mulThreadPool; @property(nonatomic,strong)dispatch_queue_t mulThreadPoolQueue; /** 线程池执行状态标识 */ @property(nonatomic,assign)BOOL threadPoolFlag; /** 递归锁. */ //@property (nonatomic, strong) NSRecursiveLock *threadLock; /** 记录注册监听数据变化的block. */ @property (nonatomic,strong) NSMutableDictionary* changeBlocks; /** 存放当队列处于忙时的事务block */ @property (nonatomic,strong) NSMutableArray* transactionBlocks; @end static BGDB* BGdb = nil; @implementation BGDB -(void)dealloc{ //烧毁数据. [self destroy]; } -(void)destroy{ if (self.changeBlocks){ [self.changeBlocks removeAllObjects];//清除所有注册列表. _changeBlocks = nil; } if (_semaphore) { _semaphore = 0x00; } [self closeDB]; if (BGdb) { BGdb = nil; } } -(NSMutableArray *)mulThreadPool{ if (_mulThreadPool == nil) { _mulThreadPool = [NSMutableArray array]; } return _mulThreadPool; } -(dispatch_queue_t)mulThreadPoolQueue{ if (_mulThreadPoolQueue == nil) { _mulThreadPoolQueue = dispatch_queue_create("BGFMDB.excuteThreadPool", DISPATCH_QUEUE_SERIAL); } return _mulThreadPoolQueue; } /** 关闭数据库. */ -(void)closeDB{ if(_disableCloseDB)return;//不关闭数据库 dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); if(!_inTransaction && _queue) {//没有事务的情况下就关闭数据库. [_queue close];//关闭数据库. _queue = nil; } dispatch_semaphore_signal(self.semaphore); } /** 删除数据库文件. */ +(BOOL)deleteSqlite:(NSString*)sqliteName{ NSString* filePath = CachePath(([NSString stringWithFormat:@"%@.db",sqliteName])); NSFileManager * file_manager = [NSFileManager defaultManager]; NSError* error; if ([file_manager fileExistsAtPath:filePath]) { [file_manager removeItemAtPath:filePath error:&error]; } return error==nil; } -(instancetype)init{ self = [super init]; if (self) { //创建递归锁. //self.threadLock = [[NSRecursiveLock alloc] init]; //创建信号量. self.semaphore = dispatch_semaphore_create(1); } return self; } -(FMDatabaseQueue *)queue{ if(_queue)return _queue; //获得沙盒中的数据库文件名 NSString* name; if(_sqliteName) { name = [NSString stringWithFormat:@"%@.db",_sqliteName]; }else{ name = SQLITE_NAME; } NSString *filename = CachePath(name); //HLog(@"数据库路径 = %@",filename); _queue = [FMDatabaseQueue databaseQueueWithPath:filename]; return _queue; } -(NSMutableDictionary *)changeBlocks{ if (_changeBlocks == nil) { @synchronized(self){ if(_changeBlocks == nil){ _changeBlocks = [NSMutableDictionary dictionary]; } } } return _changeBlocks; } -(NSMutableArray *)transactionBlocks{ if (_transactionBlocks == nil){ @synchronized(self){ if(_transactionBlocks == nil){ _transactionBlocks = [NSMutableArray array]; } } } return _transactionBlocks; } /** 获取单例函数. */ +(_Nonnull instancetype)shareManager{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ BGdb = [[BGDB alloc] init]; }); return BGdb; } /** 添加操作到线程池 */ -(void)addToThreadPool:(void (^_Nonnull)())block{ if(!self.mulThreadPoolQueue){ HLog(@"\n\n\n\n\n\n\n addToThreadPool error"); NSString *errorstr = [[NSString alloc] initWithFormat:@"addToThreadPool error block:%@,mulThreadPool:%@,mulThreadPoolQueue:%@",block,self.mulThreadPool,self.mulThreadPoolQueue ]; [cachesFileManager writeLogsWithMsg:errorstr]; //return; } NSAssert(block, @"block is nil!"); dispatch_async(self.mulThreadPoolQueue, ^{ NSString* key = [NSString stringWithFormat:@"b_%@_%@",@([[NSDate new] timeIntervalSince1970]),@(random())]; NSDictionary* dict = @{key:block}; [self.mulThreadPool addObject:dict]; [self excuteThreadPool]; }); } /** 执行线程池操作 */ -(void)excuteThreadPool{ if (!_threadPoolFlag) { _threadPoolFlag = YES; do{ if(self.mulThreadPool.count>0){ NSDictionary* dict = self.mulThreadPool.firstObject; NSString* key = [dict.allKeys firstObject]; void(^block)(void) = dict[key]; if(block){ block(); } if(self.mulThreadPool.count>0){//hxd add 20240305 推测线程没保护好 [self.mulThreadPool removeObjectAtIndex:0];//移除任务 } } }while(self.mulThreadPool.count>0); _threadPoolFlag = NO; } } //事务操作 -(void)inTransaction:(BOOL (^_Nonnull)())block{ NSAssert(block, @"block is nil!"); if([NSThread currentThread].isMainThread){//主线程直接执行 [self executeTransation:block]; }else{//子线程则延迟执行 [self.transactionBlocks addObject:block]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.2*NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self executeTransationBlocks]; }); } } /* 执行事务操作 */ -(void)executeTransation:(BOOL (^_Nonnull)())block{ [self executeDB:^(FMDatabase * _Nonnull db) { _inTransaction = db.isInTransaction; if (!_inTransaction) { _inTransaction = [db beginTransaction]; } BOOL isCommit = NO; isCommit = block(); if (_inTransaction){ if (isCommit) { [db commit]; }else { [db rollback]; } _inTransaction = NO; } }]; } -(void)executeTransationBlocks{ //[self.threadLock lock]; @synchronized(self){ if(_inTransaction || _queue){ if(self.transactionBlocks.count) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.2*NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self executeTransationBlocks]; }); } return; } while(self.transactionBlocks.count) { BOOL (^block)() = [self.transactionBlocks lastObject]; [self executeTransation:block]; [self.transactionBlocks removeLastObject]; } } //[self.threadLock unlock]; } /** 为了对象层的事物操作而封装的函数. */ -(void)executeDB:(void (^_Nonnull)(FMDatabase *_Nonnull db))block{ NSAssert(block, @"block is nil!"); if (_db){//为了事务操作防止死锁而设置. block(_db); return; } __weak typeof(self) weakSelf = self; [self.queue inDatabase:^(FMDatabase *db){ __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.db = db; block(db); strongSelf.db = nil; }]; } /** 注册数据变化监听. */ -(BOOL)registerChangeWithName:(NSString* const _Nonnull)name block:(bg_changeBlock)block{ if ([self.changeBlocks.allKeys containsObject:name]){ NSString* reason = [NSString stringWithFormat:@"%@表注册监听重复,注册监听失败!",name]; bg_debug(reason); return NO; }else{ [self.changeBlocks setObject:block forKey:name]; return YES; } } /** 移除数据变化监听. */ -(BOOL)removeChangeWithName:(NSString* const _Nonnull)name{ if ([self.changeBlocks.allKeys containsObject:name]){ [self.changeBlocks removeObjectForKey:name]; return YES; }else{ NSString* reason = [NSString stringWithFormat:@"%@表还没有注册监听,移除监听失败!",name]; bg_debug(reason); return NO; } } -(void)doChangeWithName:(NSString* const _Nonnull)name flag:(BOOL)flag state:(bg_changeState)state{ if(flag && self.changeBlocks.count>0){ //开一个子线程去执行block,防止死锁. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0), ^{ [self.changeBlocks enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop){ NSString* tablename = [key componentsSeparatedByString:@"*"].firstObject; if([name isEqualToString:tablename]){ void(^block)(bg_changeState) = obj; //返回主线程回调. dispatch_sync(dispatch_get_main_queue(), ^{ block(state); }); } }]; }); } } /** 数据库中是否存在表. */ -(void)isExistWithTableName:(NSString* _Nonnull)name complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { result = [db tableExists:name]; }]; bg_completeBlock(result); } /** 对用户暴露的 */ -(BOOL)bg_isExistWithTableName:( NSString* _Nonnull)name{ dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); NSAssert(name,@"表名不能为空!"); __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { result = [db tableExists:name]; }]; dispatch_semaphore_signal(self.semaphore); return result; } /** 创建表(如果存在则不创建). */ -(void)createTableWithTableName:(NSString* _Nonnull)name keys:(NSArray* _Nonnull)keys unionPrimaryKeys:(NSArray* _Nullable)unionPrimaryKeys uniqueKeys:(NSArray* _Nullable)uniqueKeys complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); NSAssert(keys,@"字段数组不能为空!"); //创表 __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { NSString* header = [NSString stringWithFormat:@"create table if not exists %@ (",name]; NSMutableString* sql = [[NSMutableString alloc] init]; [sql appendString:header]; NSInteger uniqueKeyFlag = uniqueKeys.count; NSMutableArray* tempUniqueKeys = [NSMutableArray arrayWithArray:uniqueKeys]; for(int i=0;i* _Nonnull)dictArray complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { [db beginTransaction]; __block NSInteger counter = 0; [dictArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull dict, NSUInteger idx, BOOL * _Nonnull stop) { @autoreleasepool { NSArray* keys = dict.allKeys; NSArray* values = dict.allValues; NSMutableString* SQL = [[NSMutableString alloc] init]; [SQL appendFormat:@"insert into %@(",name]; for(int i=0;i* _Nonnull)dictArray complete:(bg_complete_B)complete{ __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { [db beginTransaction]; __block NSInteger counter = 0; [dictArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull dict, NSUInteger idx, BOOL * _Nonnull stop) { @autoreleasepool { NSArray* uniqueKeys = [BGTool executeSelector:bg_uniqueKeysSelector forClass:cla]; NSMutableDictionary* tempDict = [[NSMutableDictionary alloc] initWithDictionary:dict]; NSMutableString* where = [NSMutableString new]; if(uniqueKeys.count > 1){ [where appendString:@" where"]; [uniqueKeys enumerateObjectsUsingBlock:^(NSString* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop){ NSString* uniqueKey = bg_sqlKey(obj); id uniqueKeyVlaue = tempDict[uniqueKey]; if(idx < (uniqueKeys.count-1)){ [where appendFormat:@" %@=%@ or",uniqueKey,bg_sqlValue(uniqueKeyVlaue)]; }else{ [where appendFormat:@" %@=%@",uniqueKey,bg_sqlValue(uniqueKeyVlaue)]; } [tempDict removeObjectForKey:uniqueKey]; }]; }else if(uniqueKeys.count == 1){ NSString* uniqueKey = bg_sqlKey([uniqueKeys firstObject]); id uniqueKeyVlaue = tempDict[uniqueKey]; [where appendFormat:@" where %@=%@",uniqueKey,bg_sqlValue(uniqueKeyVlaue)]; [tempDict removeObjectForKey:uniqueKey]; }else if([dict.allKeys containsObject:bg_sqlKey(bg_primaryKey)]){ NSString* primaryKey = bg_sqlKey(bg_primaryKey); id primaryKeyVlaue = tempDict[primaryKey]; [where appendFormat:@" where %@=%@",primaryKey,bg_sqlValue(primaryKeyVlaue)]; [tempDict removeObjectForKey:primaryKey]; }else; NSMutableArray* arguments = [NSMutableArray array]; NSMutableString* SQL = [[NSMutableString alloc] init]; [SQL appendFormat:@"update %@ set ",name]; [tempDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { [SQL appendFormat:@"%@=?,",key]; [arguments addObject:obj]; }]; SQL = [NSMutableString stringWithString:[SQL substringToIndex:SQL.length-1]]; if(where.length) { [SQL appendString:where]; } bg_debug(SQL); BOOL flag = [db executeUpdate:SQL withArgumentsInArray:arguments]; if(flag){ counter++; } } }]; if (dictArray.count == counter){ result = YES; [db commit]; }else{ result = NO; [db rollback]; } }]; //数据监听执行函数 [self doChangeWithName:name flag:result state:bg_update]; bg_completeBlock(result); } /** 批量插入或更新. */ -(void)bg_saveOrUpdateWithTableName:(NSString* _Nonnull)tablename class:(__unsafe_unretained _Nonnull Class)cla DictArray:(NSArray* _Nonnull)dictArray complete:(bg_complete_B)complete{ __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { [db beginTransaction]; __block NSInteger counter = 0; [dictArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull dict, NSUInteger idx, BOOL * _Nonnull stop) { @autoreleasepool { NSString* bg_id = bg_sqlKey(bg_primaryKey); //获得"唯一约束" NSArray* uniqueKeys = [BGTool executeSelector:bg_uniqueKeysSelector forClass:cla]; //获得"联合主键" NSArray* unionPrimaryKeys =[BGTool executeSelector:bg_unionPrimaryKeysSelector forClass:cla]; NSMutableDictionary* tempDict = [[NSMutableDictionary alloc] initWithDictionary:dict]; NSMutableString* where = [NSMutableString new]; BOOL isSave = NO;//是否存储还是更新. if(uniqueKeys.count || unionPrimaryKeys.count){ NSArray* tempKeys; NSString* orAnd; if(unionPrimaryKeys.count){ tempKeys = unionPrimaryKeys; orAnd = @"and"; }else{ tempKeys = uniqueKeys; orAnd = @"or"; } if(tempKeys.count == 1){ NSString* tempkey = bg_sqlKey([tempKeys firstObject]); id tempkeyVlaue = tempDict[tempkey]; [where appendFormat:@" where %@=%@",tempkey,bg_sqlValue(tempkeyVlaue)]; }else{ [where appendString:@" where"]; [tempKeys enumerateObjectsUsingBlock:^(NSString* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop){ NSString* tempkey = bg_sqlKey(obj); id tempkeyVlaue = tempDict[tempkey]; if(idx < (tempKeys.count-1)){ [where appendFormat:@" %@=%@ %@",tempkey,bg_sqlValue(tempkeyVlaue),orAnd]; }else{ [where appendFormat:@" %@=%@",tempkey,bg_sqlValue(tempkeyVlaue)]; } }]; } NSString* dataCountSql = [NSString stringWithFormat:@"select count(*) from %@%@",tablename,where]; __block NSInteger dataCount = 0; [db executeStatements:dataCountSql withResultBlock:^int(NSDictionary *resultsDictionary) { dataCount = [[resultsDictionary.allValues lastObject] integerValue]; return 0; }]; bg_debug(dataCountSql); if(dataCount){ //更新操作 [tempKeys enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [tempDict removeObjectForKey:bg_sqlKey(obj)]; }]; }else{ //插入操作 isSave = YES; } }else{ if([tempDict.allKeys containsObject:bg_id]){ //更新操作 id primaryKeyVlaue = tempDict[bg_id]; [where appendFormat:@" where %@=%@",bg_id,bg_sqlValue(primaryKeyVlaue)]; }else{ //插入操作 isSave = YES; } } NSMutableString* SQL = [[NSMutableString alloc] init]; NSMutableArray* arguments = [NSMutableArray array]; if(isSave){//存储操作 NSInteger num = [self getKeyMaxForTable:tablename key:bg_id db:db]; [tempDict setValue:@(num+1) forKey:bg_id]; [SQL appendFormat:@"insert into %@(",tablename]; NSArray* keys = tempDict.allKeys; NSArray* values = tempDict.allValues; for(int i=0;i* _Nullable)keys where:(NSArray* _Nullable)where complete:(bg_complete_A)complete{ NSAssert(name,@"表名不能为空!"); NSMutableArray* arrM = [[NSMutableArray alloc] init]; __block NSArray* arguments; [self executeDB:^(FMDatabase * _Nonnull db) { NSMutableString* SQL = [[NSMutableString alloc] init]; [SQL appendString:@"select"]; if ((keys!=nil)&&(keys.count>0)) { [SQL appendString:@" "]; for(int i=0;i0)){ NSArray* results = [BGTool where:where]; [SQL appendString:results[0]]; arguments = results[1]; } bg_debug(SQL); // 1.查询数据 FMResultSet *rs = [db executeQuery:SQL withArgumentsInArray:arguments]; if (rs == nil) { bg_debug(@"查询错误,可能是'类变量名'发生了改变或'字段','表格'不存在!,请存储后再读取,或检查条件数组'字段名称'是否正确"); } // 2.遍历结果集 while (rs.next) { NSMutableDictionary* dictM = [[NSMutableDictionary alloc] init]; for (int i=0;i<[[[rs columnNameToIndexMap] allKeys] count];i++) { dictM[[rs columnNameForIndex:i]] = [rs objectForColumnIndex:i]; } [arrM addObject:dictM]; } //查询完后要关闭rs,不然会报@"Warning: there is at least one open result set around after performing [rs close]; }]; bg_completeBlock(arrM); } /** 查询对象. */ -(void)queryWithTableName:(NSString* _Nonnull)name where:(NSString* _Nullable)where complete:(bg_complete_A)complete{ NSAssert(name,@"表名不能为空!"); NSMutableArray* arrM = [[NSMutableArray alloc] init]; [self executeDB:^(FMDatabase * _Nonnull db) { NSMutableString* SQL = [NSMutableString string]; [SQL appendFormat:@"select * from %@",name]; !where?:[SQL appendFormat:@" %@",where]; bg_debug(SQL); // 1.查询数据 FMResultSet *rs = [db executeQuery:SQL]; if (rs == nil) { bg_debug(@"查询错误,'表格'不存在!,请存储后再读取!"); } // 2.遍历结果集 while (rs.next) { NSMutableDictionary* dictM = [[NSMutableDictionary alloc] init]; for (int i=0;i<[[[rs columnNameToIndexMap] allKeys] count];i++) { dictM[[rs columnNameForIndex:i]] = [rs objectForColumnIndex:i]; } [arrM addObject:dictM]; } //查询完后要关闭rs,不然会报@"Warning: there is at least one open result set around after performing [rs close]; }]; bg_completeBlock(arrM); } /** 更新数据. */ -(void)updateWithTableName:(NSString* _Nonnull)name valueDict:(NSDictionary* _Nonnull)valueDict where:(NSArray* _Nullable)where complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); NSAssert(valueDict,@"更新数据集合不能为空!"); __block BOOL result; NSMutableArray* arguments = [NSMutableArray array]; [self executeDB:^(FMDatabase * _Nonnull db) { NSMutableString* SQL = [[NSMutableString alloc] init]; [SQL appendFormat:@"update %@ set ",name]; for(int i=0;i0)){ NSArray* results = [BGTool where:where]; [SQL appendString:results[0]]; [arguments addObjectsFromArray:results[1]]; } bg_debug(SQL); result = [db executeUpdate:SQL withArgumentsInArray:arguments]; }]; //数据监听执行函数 [self doChangeWithName:name flag:result state:bg_update]; bg_completeBlock(result); } -(void)updateQueueWithTableName:(NSString* _Nonnull)name valueDict:(NSDictionary* _Nullable)valueDict conditions:(NSString* _Nonnull)conditions complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db){ NSString* SQL; if (!valueDict || !valueDict.count) { SQL = [NSString stringWithFormat:@"update %@ %@",name,conditions]; }else{ NSMutableString* param = [NSMutableString stringWithFormat:@"update %@ set ",name]; for(int i=0;i0)){ NSArray* results = [BGTool where:where]; [SQL appendString:results[0]]; [arguments addObjectsFromArray:results[1]]; } bg_debug(SQL); result = [db executeUpdate:SQL withArgumentsInArray:arguments]; }]; //数据监听执行函数 [self doChangeWithName:name flag:result state:bg_delete]; bg_completeBlock(result); } -(void)deleteQueueWithTableName:(NSString* _Nonnull)name conditions:(NSString* _Nullable)conditions complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { NSString* SQL = conditions?[NSString stringWithFormat:@"delete from %@ %@",name,conditions]:[NSString stringWithFormat:@"delete from %@",name]; bg_debug(SQL); result = [db executeUpdate:SQL]; }]; //数据监听执行函数 [self doChangeWithName:name flag:result state:bg_delete]; bg_completeBlock(result); } /** 直接传入条件sql语句删除. */ -(void)deleteWithTableName:(NSString* _Nonnull)name conditions:(NSString* _Nullable)conditions complete:(bg_complete_B)complete{ dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); [self deleteQueueWithTableName:name conditions:conditions complete:complete]; dispatch_semaphore_signal(self.semaphore); } -(void)deleteQueueWithTableName:(NSString* _Nonnull)name forKeyPathAndValues:(NSArray* _Nonnull)keyPathValues complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); NSString* like = [BGTool getLikeWithKeyPathAndValues:keyPathValues where:YES]; __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { NSMutableString* SQL = [[NSMutableString alloc] init]; [SQL appendFormat:@"delete from %@%@",name,like]; bg_debug(SQL); result = [db executeUpdate:SQL]; }]; //数据监听执行函数 [self doChangeWithName:name flag:result state:bg_delete]; bg_completeBlock(result); } //根据keypath删除表内容. -(void)deleteWithTableName:(NSString* _Nonnull)name forKeyPathAndValues:(NSArray* _Nonnull)keyPathValues complete:(bg_complete_B)complete{ dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); [self deleteQueueWithTableName:name forKeyPathAndValues:keyPathValues complete:complete]; dispatch_semaphore_signal(self.semaphore); } /** 根据表名删除表格全部内容. */ -(void)clearTable:(NSString* _Nonnull)name complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { NSString* SQL = [NSString stringWithFormat:@"delete from %@",name]; bg_debug(SQL); result = [db executeUpdate:SQL]; }]; //数据监听执行函数 [self doChangeWithName:name flag:result state:bg_delete]; bg_completeBlock(result); } /** 删除表. */ -(void)dropTable:(NSString* _Nonnull)name complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { NSString* SQL = [NSString stringWithFormat:@"drop table %@",name]; bg_debug(SQL); result = [db executeUpdate:SQL]; }]; //数据监听执行函数 [self doChangeWithName:name flag:result state:bg_drop]; bg_completeBlock(result); } /** 删除表(线程安全). */ -(void)dropSafeTable:(NSString* _Nonnull)name complete:(bg_complete_B)complete{ dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); @autoreleasepool { [self dropTable:name complete:complete]; } dispatch_semaphore_signal(self.semaphore); } /** 动态添加表字段. */ -(void)addTable:(NSString* _Nonnull)name key:(NSString* _Nonnull)key complete:(bg_complete_B)complete{ NSAssert(name,@"表名不能为空!"); __block BOOL result; [self executeDB:^(FMDatabase * _Nonnull db) { NSString* SQL = [NSString stringWithFormat:@"alter table %@ add %@;",name,[BGTool keyAndType:key]]; bg_debug(SQL); result = [db executeUpdate:SQL]; }]; bg_completeBlock(result); } /** 查询该表中有多少条数据 */ -(NSInteger)countQueueForTable:(NSString* _Nonnull)name where:(NSArray* _Nullable)where{ NSAssert(name,@"表名不能为空!"); NSAssert(!(where.count%3),@"条件数组错误!"); NSMutableString* strM = [NSMutableString string]; !where?:[strM appendString:@" where "]; for(int i=0;i* const _Nonnull)keys complete:(bg_complete_I)complete{ //获取"唯一约束"字段名 NSArray* uniqueKeys = [BGTool executeSelector:bg_uniqueKeysSelector forClass:cla]; //获取“联合主键”字段名 NSArray* unionPrimaryKeys = [BGTool executeSelector:bg_unionPrimaryKeysSelector forClass:cla]; //建立一张临时表 __block BOOL createFlag; [self createTableWithTableName:B keys:keys unionPrimaryKeys:unionPrimaryKeys uniqueKeys:uniqueKeys complete:^(BOOL isSuccess) { createFlag = isSuccess; }]; if (!createFlag){ bg_debug(@"数据库更新失败!"); bg_completeBlock(bg_error); return; } __block bg_dealState refreshstate = bg_error; __block BOOL recordError = NO; __block BOOL recordSuccess = NO; __weak typeof(self) BGSelf = self; NSInteger count = [self countQueueForTable:A where:nil]; for(NSInteger i=0;i* const _Nonnull)keys complete:(bg_complete_I)complete{ NSAssert(name,@"表名不能为空!"); NSAssert(keys,@"字段数组不能为空!"); [self isExistWithTableName:name complete:^(BOOL isSuccess){ if (!isSuccess){ bg_debug(@"没有数据存在,数据库更新失败!"); bg_completeBlock(bg_error); return; } }]; NSString* BGTempTable = @"BGTempTable"; //事务操作. __block int recordFailCount = 0; [self executeTransation:^BOOL{ [self copyA:name toB:BGTempTable class:cla keys:keys complete:^(bg_dealState result) { if(result == bg_complete){ recordFailCount++; } }]; [self dropTable:name complete:^(BOOL isSuccess) { if(isSuccess)recordFailCount++; }]; [self copyA:BGTempTable toB:name class:cla keys:keys complete:^(bg_dealState result) { if(result == bg_complete){ recordFailCount++; } }]; [self dropTable:BGTempTable complete:^(BOOL isSuccess) { if(isSuccess)recordFailCount++; }]; if(recordFailCount != 4){ bg_debug(@"发生错误,更新数据库失败!"); } return recordFailCount==4; }]; //回调结果. if (recordFailCount==0) { bg_completeBlock(bg_error); }else if (recordFailCount>0&&recordFailCount<4){ bg_completeBlock(bg_incomplete); }else{ bg_completeBlock(bg_complete); } } /** 刷新数据库,即将旧数据库的数据复制到新建的数据库,这是为了去掉没用的字段. */ -(void)refreshTable:(NSString* _Nonnull)name class:(__unsafe_unretained _Nonnull Class)cla keys:(NSArray* const _Nonnull)keys complete:(bg_complete_I)complete{ dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); @autoreleasepool { [self refreshQueueTable:name class:cla keys:keys complete:complete]; } dispatch_semaphore_signal(self.semaphore); } -(void)copyA:(NSString* _Nonnull)A toB:(NSString* _Nonnull)B keyDict:(NSDictionary* const _Nullable)keyDict complete:(bg_complete_I)complete{ //获取"唯一约束"字段名 NSArray* uniqueKeys = [BGTool executeSelector:bg_uniqueKeysSelector forClass:NSClassFromString(A)]; //获取“联合主键”字段名 NSArray* unionPrimaryKeys = [BGTool executeSelector:bg_unionPrimaryKeysSelector forClass:NSClassFromString(A)]; __block NSArray* keys = [BGTool getClassIvarList:NSClassFromString(A) Object:nil onlyKey:NO]; NSArray* newKeys = keyDict.allKeys; NSArray* oldKeys = keyDict.allValues; //建立一张临时表 __block BOOL createFlag; [self createTableWithTableName:B keys:keys unionPrimaryKeys:unionPrimaryKeys uniqueKeys:uniqueKeys complete:^(BOOL isSuccess) { createFlag = isSuccess; }]; if (!createFlag){ bg_debug(@"数据库更新失败!"); bg_completeBlock(bg_error); return; } __block bg_dealState refreshstate = bg_error; __block BOOL recordError = NO; __block BOOL recordSuccess = NO; __weak typeof(self) BGSelf = self; NSInteger count = [self countQueueForTable:A where:nil]; for(NSInteger i=0;i0&&recordFailCount<4){ bg_completeBlock(bg_incomplete); }else{ bg_completeBlock(bg_complete); } } -(void)refreshTable:(NSString* _Nonnull)name class:(__unsafe_unretained _Nonnull Class)cla keys:(NSArray* const _Nonnull)keys keyDict:(NSDictionary* const _Nonnull)keyDict complete:(bg_complete_I)complete{ dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); @autoreleasepool { [self refreshQueueTable:name class:cla keys:keys keyDict:keyDict complete:complete]; } dispatch_semaphore_signal(self.semaphore); } /** 判断类属性是否有改变,智能刷新. */ -(void)ifIvarChangeForObject:(id)object ignoredKeys:(NSArray*)ignoredkeys{ //获取缓存的属性信息 NSCache* cache = [NSCache bg_cache]; NSString *tableName = [object valueForKey:bg_tableNameKey]; tableName = tableName.length ? tableName : NSStringFromClass([object class]); NSString* cacheKey = [NSString stringWithFormat:@"%@_IvarChangeState",tableName]; id IvarChangeState = [cache objectForKey:cacheKey]; if(IvarChangeState){ return; }else{ [cache setObject:@(YES) forKey:cacheKey]; } @autoreleasepool { //获取表名 NSString* tableName = [BGTool getTableNameWithObject:object]; NSMutableArray* newKeys = [NSMutableArray array]; NSMutableArray* sqlKeys = [NSMutableArray array]; [self executeDB:^(FMDatabase * _Nonnull db) { NSString* SQL = [NSString stringWithFormat:@"select sql from sqlite_master where tbl_name='%@' and type='table';",tableName]; NSMutableArray* tempArrayM = [NSMutableArray array]; //获取表格所有列名. [db executeStatements:SQL withResultBlock:^int(NSDictionary *resultsDictionary) { NSString* allName = [resultsDictionary.allValues lastObject]; allName = [allName stringByReplacingOccurrencesOfString:@"\"" withString:@""]; NSRange range1 = [allName rangeOfString:@"("]; allName = [allName substringFromIndex:range1.location+1]; NSRange range2 = [allName rangeOfString:@")"]; allName = [allName substringToIndex:range2.location]; NSArray* sqlNames = [allName componentsSeparatedByString:@","]; for(NSString* sqlName in sqlNames){ NSString* columnName = [[sqlName componentsSeparatedByString:@" "] firstObject]; [tempArrayM addObject:columnName]; } return 0; }]; NSArray* columNames = tempArrayM.count?tempArrayM:nil; NSArray* keyAndtypes = [BGTool getClassIvarList:[object class] Object:object onlyKey:NO]; for(NSString* keyAndtype in keyAndtypes){ NSString* key = [[keyAndtype componentsSeparatedByString:@"*"] firstObject]; if(ignoredkeys && [ignoredkeys containsObject:key])continue; key = [NSString stringWithFormat:@"%@%@",BG,key]; if (![columNames containsObject:key]) { [newKeys addObject:keyAndtype]; } } NSMutableArray* keys = [NSMutableArray arrayWithArray:[BGTool getClassIvarList:[object class] Object:nil onlyKey:YES]]; if (ignoredkeys) { [keys removeObjectsInArray:ignoredkeys]; } [columNames enumerateObjectsUsingBlock:^(NSString* _Nonnull columName, NSUInteger idx, BOOL * _Nonnull stop) { NSString* propertyName = [columName stringByReplacingOccurrencesOfString:BG withString:@""]; if(![keys containsObject:propertyName]){ [sqlKeys addObject:columName]; } }]; }]; if((sqlKeys.count==0) && (newKeys.count>0)){ //此处只是增加了新的列. for(NSString* key in newKeys){ //添加新字段 [self addTable:tableName key:key complete:^(BOOL isSuccess){}]; } }else if(sqlKeys.count>0){ //字段发生改变,减少或名称变化,实行刷新数据库. NSMutableArray* newTableKeys = [[NSMutableArray alloc] initWithArray:[BGTool getClassIvarList:[object class] Object:nil onlyKey:NO]]; NSMutableArray* tempIgnoreKeys = [[NSMutableArray alloc] initWithArray:ignoredkeys]; for(int i=0;i%@",@(index)] complete:^(BOOL isSuccess) { if(isSuccess) { flag++; } }]; } return flag==2; }]; } dispatch_semaphore_signal(self.semaphore); return flag==2; } #pragma mark 存储字典. /** 直接存储字典. */ -(void)saveDictionary:(NSDictionary* _Nonnull)dictionary complete:(bg_complete_B)complete{ NSAssert(dictionary||dictionary.allKeys.count,@"字典不能为空!"); dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); @autoreleasepool { __weak typeof(self) BGSelf = self; NSString* const tableName = @"BG_Dictionary"; [self isExistWithTableName:tableName complete:^(BOOL isSuccess) { if (!isSuccess) { [BGSelf createTableWithTableName:tableName keys:@[[NSString stringWithFormat:@"%@*i",bg_primaryKey],@"key*@\"NSString\"",@"value*@\"NSString\""] unionPrimaryKeys:nil uniqueKeys:@[@"key"] complete:nil]; } }]; __block NSInteger num = 0; [self executeTransation:^BOOL{ [dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop){ NSString* type = [NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([value class])]; id sqlValue = [BGTool getSqlValue:value type:type encode:YES]; sqlValue = [NSString stringWithFormat:@"%@$$$%@",sqlValue,type]; NSDictionary* dict = @{@"BG_key":key,@"BG_value":sqlValue}; [self insertIntoTableName:tableName Dict:dict complete:^(BOOL isSuccess) { if(isSuccess) { num++; } }]; }]; return YES; }]; bg_completeBlock(dictionary.allKeys.count==num); } dispatch_semaphore_signal(self.semaphore); } /** 添加字典元素. */ -(BOOL)bg_setValue:(id _Nonnull)value forKey:(NSString* const _Nonnull)key{ NSAssert(key,@"key不能为空!"); NSAssert(value,@"value不能为空!"); NSDictionary* dict = @{key:value}; __block BOOL result; [self saveDictionary:dict complete:^(BOOL isSuccess) { result = isSuccess; }]; return result; } /** 更新字典元素. */ -(BOOL)bg_updateValue:(id _Nonnull)value forKey:(NSString* const _Nonnull)key{ NSAssert(key,@"key不能为空!"); NSAssert(value,@"value不能为空!"); dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); __block BOOL result; @autoreleasepool{ NSString* type = [NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([value class])]; id sqlvalue = [BGTool getSqlValue:value type:type encode:YES]; sqlvalue = [NSString stringWithFormat:@"%@$$$%@",sqlvalue,type]; NSDictionary* dict = @{@"BG_value":sqlvalue}; NSString* const tableName = @"BG_Dictionary"; [self updateWithTableName:tableName valueDict:dict where:@[@"key",@"=",key] complete:^(BOOL isSuccess) { result = isSuccess; }]; } dispatch_semaphore_signal(self.semaphore); return result; } /** 遍历字典元素. */ -(void)bg_enumerateKeysAndObjectsUsingBlock:(void (^ _Nonnull)(NSString* _Nonnull key, id _Nonnull value,BOOL *stop))block{ dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); @autoreleasepool{ NSString* const tableName = @"BG_Dictionary"; NSString* condition = [NSString stringWithFormat:@"order by %@ asc",bg_sqlKey(bg_primaryKey)]; [self queryQueueWithTableName:tableName conditions:condition complete:^(NSArray * _Nullable array) { BOOL stopFlag = NO; for(NSDictionary* dict in array){ NSArray* keyAndTypes = [dict[@"BG_value"] componentsSeparatedByString:@"$$$"]; NSString* key = dict[@"BG_key"]; id value = [keyAndTypes firstObject]; NSString* type = [keyAndTypes lastObject]; value = [BGTool getSqlValue:value type:type encode:NO]; !block?:block(key,value,&stopFlag); if(stopFlag){ break; } } }]; } dispatch_semaphore_signal(self.semaphore); } /** 获取字典元素. */ -(id _Nullable)bg_valueForKey:(NSString* const _Nonnull)key{ NSAssert(key,@"key不能为空!"); dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); __block id resultValue = nil; @autoreleasepool { NSString* const tableName = @"BG_Dictionary"; [self queryQueueWithTableName:tableName conditions:[NSString stringWithFormat:@"where BG_key='%@'",key] complete:^(NSArray * _Nullable array){ if(array&&array.count){ NSDictionary* dict = [array firstObject]; NSArray* keyAndTypes = [dict[@"BG_value"] componentsSeparatedByString:@"$$$"]; id value = [keyAndTypes firstObject]; NSString* type = [keyAndTypes lastObject]; resultValue = [BGTool getSqlValue:value type:type encode:NO]; } }]; } dispatch_semaphore_signal(self.semaphore); return resultValue; } /** 删除字典元素. */ -(BOOL)bg_deleteValueForKey:(NSString* const _Nonnull)key{ NSAssert(key,@"key不能为空!"); dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); __block BOOL result; @autoreleasepool { NSString* const tableName = @"BG_Dictionary"; [self deleteQueueWithTableName:tableName conditions:[NSString stringWithFormat:@"where BG_key='%@'",key] complete:^(BOOL isSuccess) { result = isSuccess; }]; } dispatch_semaphore_signal(self.semaphore); return result; } @end