范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

开源一个轻量级iOS数据库自动升级的管理类

  SpeSqliteManager4IOS
  一个轻量级管理iOS数据自动升级的管理类,无需第三方组件支持。
  设计亮点
  1.以静制动:新增数据库表、字段、删除表都是修改plist即可,保持代码稳定性。
  2.方便可控:提供加密/非加密两种数据库操作方式,根据需求灵活配置一个宏即可。
  3.数据迁移:由非加密升级为加密方式,手动配置下加密版本号,即可自动提供数据迁移。
  使用说明
  1.目前写的逻辑只是用来只创建了一个db文件。
  2.关于plist:
  1.dbName:是保存到沙盒的数据库文件的名称。
  2.dbVersion:数据库版本号,判断本地数据库文件是否升级就通过此key。
  3.dbTables:想要创建的表名,每个表名下是具体的字段。
  4.上面3个字段名最好不要改,如果修改了话,连同程序里的宏也请同时修改下。
  3.对于已经已使用plist的应用,注意plist中的值不要乱改动。
  4.修改表时:1.不用的表和字段作为冗余表和字段,不删。
  关键代码 1.plist <?xml version="1.0" encoding="UTF-8"?>    	dbName 	local.db 	dbVersion 	1 	app 	 		 			chat 			 				 					id 					INTEGER PRIMARY KEY AUTOINCREMENT 				 				 					uid 					TEXT 				 			 		    2.SpeIOSSqliteUpdateManager(核心管理类)  #import "SpeEncryptDefine.h" #if ENCRPTYED     #import  #else     #import  #endif  @interface SpeIOSSqliteUpdateManager : NSObject {     sqlite3         *m_db;     NSDictionary    *m_sqlSettingDic;//在bundle中的数据库plist,即新plist     NSDictionary    *m_localSettingDic;//本地的数据库plist     BOOL            m_appUpdate;//冷启动是否是覆盖安装,如果是需要清除本地总的资源版本号 }  SINGLETON_FOR_HEADER(SpeIOSSqliteUpdateManager)  //创建或升级本地数据库 +(void)createOrUpdateDB;  //db名 + (NSString *)dbName;  + (sqlite3 *)db;  - (NSString *)pathLocalDB;  - (NSArray *)arrTables;  -(BOOL)execSql:(NSString *)sql;  - (BOOL)isAppUpdate; @end  // //  Created by Points on 15-04-03. //  Copyright (c) 2015年 Points. All rights reserved. //  #define KEY_DB_VERSION @"dbVersion" #define KEY_DB_NAME    @"dbName" #define KEY_TABLES     @"dbTables" #define KEY_LOCAL_NAME @"SpeSqlSetting.plist"  #define KEY_SQL_SETTING_PATH   [[NSBundle bundleForClass:self.class] pathForResource:@"SpeSqlSetting" ofType:@"plist"]  #import "SpeIOSSqliteUpdateManager.h" #import "SpeIOSSqliteUpdateManager+Backup.h" #import "SpeDesEncrypt.h" @implementation SpeIOSSqliteUpdateManager  SINGLETON_FOR_CLASS(SpeIOSSqliteUpdateManager) #if ENCRPTYED SQLITE_API int sqlite3_key(sqlite3 *db, const void *pKey, int nKey); SQLITE_API int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey); #endif   - (void)dealloc {         sqlite3_close(m_db); }  - (id)init{     if(self = [super init]){         m_appUpdate = NO;         m_sqlSettingDic = [NSDictionary dictionaryWithContentsOfFile:KEY_SQL_SETTING_PATH];         NSString *database_path = [self pathLocalDB];         NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);         NSString *documents = [paths objectAtIndex:0];         NSString *sqlPath = [documents stringByAppendingFormat:@"/lz/%@",KEY_LOCAL_NAME];                  NSString * _document = [documents stringByAppendingFormat:@"/lz"];         if(![[NSFileManager defaultManager]fileExistsAtPath: _document]){             [[NSFileManager defaultManager] createDirectoryAtPath:_document withIntermediateDirectories:YES attributes:nil error:nil];         }          [self getLocalEncryptedPlist:sqlPath];                  NSLog(@"DB路径=%@",database_path);         if([[NSFileManager defaultManager]fileExistsAtPath:database_path]){             //已经存在需要判断当前数据库版本是否小于24,如果是就需要走备份数据,再创建新数据库,再插入备份数据流程             [self startUpgrade];         }else{             [self openDB];         }         if([self isNeedUpadte]){             m_appUpdate = YES;             [self updateLocalDB];              #pragma mark  // 两次循环找出被删除的表             [self dropTheTableDeteted];                          [self encryptLoclPlist:sqlPath];         }     }     return self; }  #pragma mark  drop被删除的表(也就是对比新plist表,在本地沙盒数据库里面不存在的table) - (void)dropTheTableDeteted{     /*新plist里面所有的表名*/     NSMutableArray *m_sqlSettingTables = [NSMutableArray array];     // 获取plist文件对应的所有的key     NSArray *m_sqlSettingDic_keys = m_sqlSettingDic.allKeys;     // 遍历出 所有的value数的元素     for (NSString *key in m_sqlSettingDic_keys) {         id id_value = m_sqlSettingDic[key];         if ([id_value isKindOfClass:[NSArray class]]) {             NSArray *value_array = (NSArray *)id_value;             // 把数组里面的内容遍历出来             for (int i = 0; i 0){                 tableName = [arrKey firstObject];             }                          if(localSqlDic[tableName] == nil){                 searchCount++;             }             else{                 continue;             }         }         //是新表         if(searchCount == arr.count){             [self createTable:tableDic];         }         else{             NSArray *newSqlArr = tableDic[tableName];                 for(NSDictionary *localSqlSetDic in arrLocal){                     if([[localSqlSetDic.allKeys firstObject] isEqualToString:tableName]){                         NSArray *localSqlArr = localSqlSetDic[tableName];                         //判断是不是完全一致                         if([newSqlArr isEqualToArray:localSqlArr]){                             break;                         }                         else{                             NSArray *addColumn =  [newSqlArr subarrayWithRange:NSMakeRange(localSqlArr.count, newSqlArr.count-localSqlArr.count)];                             //更新表字段                             __block  NSMutableString *alterSql = nil;                             for(NSDictionary *addColumnDic in addColumn){                                 alterSql = [NSMutableString stringWithFormat:@"alter table %@ add column",tableName];                                 [addColumnDic enumerateKeysAndObjectsUsingBlock:^(id newParaKey, id newParaObj, BOOL * __unused stop) {                                     [alterSql appendFormat:@" %@ %@",newParaKey,newParaObj];                                     [self execSql:alterSql];                                 }];                             }                             break;                         }                     }                 }         }     } }  /**  *    @brief    创建所有表  *  *    @return  */ -(BOOL)createTable{     NSArray *arr = [self arrTables];     if(arr == nil || arr.count == 0{         return NO;     }          for(NSDictionary *tableDic in arr){         [self createTable:tableDic];     }     return YES; }  /**  *    @brief    创建表的具体逻辑  *  *    @return  */ - (void)createTable:(NSDictionary *)tableDic{     __block  NSMutableString *createSql = [NSMutableString stringWithString:@"CREATE TABLE IF NOT EXISTS"];          [tableDic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * __unused stop) {         NSArray *arrColumn = obj;         if(arrColumn == nil || arrColumn.count == 0){             return ;         }                  [createSql appendFormat:@""%@"(",key];                  for(NSDictionary *columnDic in arrColumn){             [columnDic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * __unused stop) {                                  [createSql appendFormat:@"%@ %@,",key,obj];             }];         }                  createSql = [NSMutableString stringWithString:[createSql substringToIndex:createSql.length-1]];         [createSql appendFormat:@")"];     }];     [self execSql:createSql];  }  - (void)dropTable:(NSString *)tableName{     NSString *dropSql = [NSString stringWithFormat:@"DROP TABLE %@",tableName];     BOOL result = [self execSql:dropSql];     if (!result) {         NSLog(@"drop table %@ 失败",tableName);     } }   -(BOOL)execSql:(NSString *)sql{     char *err = NULL;     if (sqlite3_exec(m_db, [sql UTF8String], NULL, NULL, &err) != SQLITE_OK){         NSLog(@"数据库操作:%@失败!====%s",sql,err);         return NO;     }     else{         NSLog(@"操作数据成功==sql:%@",sql);     }     return YES; }  - (sqlite3 *)dbHandle{     return m_db; }   + (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {     if (jsonString == nil) {         return nil;     }     NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];     NSError *err;     NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData                                                         options:NSJSONReadingMutableContainers                                                           error:&err];     if(err) {         NSLog(@"json解析失败:%@",err);         return nil;     }     return dic; }  - (NSString *)convert2JSONWithDictionary:(NSDictionary *)dic{     NSError *err;     NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:0 error:&err];     NSString *jsonString;     if (!jsonData) {         NSLog(@"%@",err);     }else{         jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];     }     NSLog(@"%@",jsonString);     return jsonString; }  - (BOOL)isAppUpdate{     return m_appUpdate; } @end  3.SpeIOSSqliteUpdateManager+Backup(数据迁移) // // //  Created by Points on 2020/5/22. //  SQLCipher无法对老数据加密。方案:备份老数据数据->删除老数据库->新建新数据库->插入备份数据->完成迁移    #import "SpeIOSSqliteUpdateManager.h"  NS_ASSUME_NONNULL_BEGIN  @interface SpeIOSSqliteUpdateManager (Backup) /// 获取要备份的数据 - (NSMutableArray *)startShiftSqlite3Data;  /// 插入到新数据库 /// @param arrDatas  之前备份的数据 - (void)insertAllWillhiftSqlite3Data:(NSArray *)arrDatas;  /// 处理本地数据库与新的plist的字段不统一的bug /**  * @pragma 本地plist的数组  */ - (void)handleDBMatchNewPlist:(NSArray *)arrLocal;  @end  NS_ASSUME_NONNULL_END  ```  ``` // //  SpeIOSSqliteUpdateManager+Backup.m // //  Created by Points on 2020/5/22. //  #import "SpeIOSSqliteUpdateManager+Backup.h"   @implementation SpeIOSSqliteUpdateManager (Backup) #pragma mark -  数据备份及重新插入新数据   /// 获取要备份的数据 - (NSMutableArray *)startShiftSqlite3Data{     NSArray *arr = [self arrTables];     NSMutableArray *arrDatas = [self arrWillhiftSqlite3Data:arr];     return arrDatas; }  - (NSMutableArray *)arrWillhiftSqlite3Data:(NSArray *)arr{     NSMutableArray *arrTotal = [NSMutableArray array];     for(NSDictionary *tableDic in arr){          NSString *tableName = [tableDic.allKeys firstObject];         BOOL flag = NULL;          NSArray *columns = [self filterPrimaryKey:[tableDic.allValues firstObject] hasPrimary:&flag];          NSMutableArray *arr = [NSMutableArray array];            @synchronized (self) {                   NSString *sql = [NSString stringWithFormat:@"SELECT * FROM "%@" ",tableName];                      sqlite3_stmt * statement;                   if (sqlite3_prepare_v2(m_db, [sql UTF8String], -1, &statement, nil) == SQLITE_OK){                          while (sqlite3_step(statement) == SQLITE_ROW){                              [arr addObject:[self row:statement columns:columns hasPrimary:flag]];                          }                      }                      sqlite3_finalize(statement);                  }         [arrTotal addObject:arr];       }     return arrTotal; }  - (NSArray *)filterPrimaryKey:(NSArray *)arrColumns hasPrimary:(BOOL *)hasPrimary{     NSMutableArray *arr = [NSMutableArray array];     *hasPrimary = NO;     for(NSDictionary *value in arrColumns){         if([[value.allValues firstObject]rangeOfString:@"PRIMARY"].length==0 && [[value.allValues firstObject]rangeOfString:@"primary"].length==0){             [arr addObject:value];         }else{             *hasPrimary = YES;         }     }     return arr; }  - (NSMutableDictionary *)row:(sqlite3_stmt *) statement columns:(NSArray *)columns  hasPrimary:(BOOL)hasPrimary {     NSMutableDictionary *data = [NSMutableDictionary dictionary];     for(NSDictionary *column in columns){         int index = (int)[columns indexOfObject:column];                  if([[column.allKeys firstObject]rangeOfString:@"PRIMARY"].length>0||[[column.allValues firstObject]rangeOfString:@"primary"].length>0){//先判断是否有主键                      }else  if([[column.allValues firstObject]isEqualToString:@"TEXT"]){//主键数据不要保存             char *v = sqlite3_column_text(statement, hasPrimary?(index+1):index);             NSString *item =  v== NULL ? @"" :[NSString stringWithCString:v  encoding:NSUTF8StringEncoding];             [data setValue:item forKey:[column.allKeys firstObject]];         }else if ([[column.allValues firstObject]isEqualToString:@"INTEGER"]){               int v = sqlite3_column_int(statement, hasPrimary?(index+1):index);               [data setValue:[NSString stringWithFormat:@"%d",v] forKey:[column.allKeys firstObject]];         }else if ([[column.allValues firstObject]isEqualToString:@"BLOB"]){             [data setValue:@"" forKey:[column.allKeys firstObject]];       }     }     return data; }   /// 插入到新数据库 /// @param arrDatas  之前备份的数据 - (void)insertAllWillhiftSqlite3Data:(NSArray *)arrDatas{         NSArray *arr = [self arrTables];          for(NSDictionary *tableDic in arr){              NSInteger index = [arr indexOfObject:tableDic];              NSString *tableName = [tableDic.allKeys firstObject];             BOOL *flag = NULL;              NSArray *columns = [self filterPrimaryKey:[tableDic.allValues firstObject] hasPrimary:&flag];              //拼接sql语句             NSMutableString *sql = [NSMutableString stringWithFormat:@"insert into "%@"",tableName];             for (NSInteger i=0; i count) {             //本地的数据库有更新             NSArray *addColumn =  [array subarrayWithRange:NSMakeRange(count, array.count-count)];             //更新表字段             __block  NSMutableString *alterSql = nil;             for(NSDictionary *addColumnDic in addColumn)             {                 alterSql = [NSMutableString stringWithFormat:@"alter table %@ add column",tableName];                 [addColumnDic enumerateKeysAndObjectsUsingBlock:^(id newParaKey, id newParaObj, BOOL * __unused stop) {                     [alterSql appendFormat:@" %@ %@",newParaKey,newParaObj];                     [self execSql:alterSql];                 }];             }         }     }      }  - (int)getColumnCount:(NSString *)tableName {     @synchronized (self) {         NSString *sqlQuery = [NSString stringWithFormat:@"select * from %@",tableName];         sqlite3_stmt * statement;         if (sqlite3_prepare_v2(m_db, [sqlQuery UTF8String], -1, &statement, nil) == SQLITE_OK) {             sqlite3_finalize(statement);             return sqlite3_column_count(statement);         }         return 0;     } }  @end  4.集成方法:  1.导入如下几个文件,在plist根据自己的业务配置数据库名、版本号、表、表字段。  2.在主application中直接调用[SqliteDataManager sharedInstance]即可,自动创建和升级数据库。
  使用效果
  1.已在生产环境使用多年,效果良好。
  喜欢的朋友可以移步至gitee查看源码:
  SpeSqliteManager4IOS: 一个用来管理iOS数据自动升级的管理类

明天正式发售!李宁巴特勒一代签名球鞋来袭,球鞋的名字不能乱叫李宁巴特勒一代签名球鞋铁血配色关注运动品牌李宁的球迷朋友们都知道,随着新赛季的大幕在昨天正式拉开,李宁为旗下核心品牌代言人吉米巴特勒筹备已久的个人第一双签名球鞋巴特勒一代,终于也将苏有朋最新时尚大片上线,换了三套造型,帅气显年轻头条创作挑战赛苏有朋是很多人心中的男神吧?出道这么多年了,带给大家很多好听的歌曲和经典的影视作品。如今49岁的他,还是那么年轻帅气,简直就是妥妥的冻龄男神。他在娱乐圈中的名气很高,套餐贵,语音电话用不完,流量不够用,怎么办?电话是用来收快递的,的确没错,现在有了网络,实现了微信语音电话,普通电话很少人打了,原来号码套餐贵,很多语音电话分钟数也用不完,但流量严重不足。加上现在的号码用了很久,也绑定了很多国足逆袭?曝足协请来图赫尔,执教目的成疑,足协没想过换帅国足一直以来是国内球迷心中的痛点,我们将全部的感情都投入到国足身上,但是后者并没有给我们应有的回应。国足近几次冲击世界杯前,我们都满怀信心,但最后都是铩羽而归,这一次卡塔尔世预赛1更大气!改款轩逸曝光,外观沿用最新家族造型,配燃油混动提到紧凑轿车就不得不说一下目前一枝独秀的日产轩逸,在国产车型新能源车型的围剿下它依然拿下来9月份的轿车销冠宝座,近些年来轩逸一直稳坐紧凑轿车销量榜的前列,可以说它是如今新能源浪潮以情感一些,让人破防的句子一在想,如果人与人之间的关系永远只停留在刚认识那会那该有多好至少,也不会拥有那么后来和酸楚了二人是会变的!这一句话,几乎说尽了爱情中所有的一切从开始到结束,从心动到绝望三有一段话我21岁拿下世界冠军他现在自己培养冠军1996年出生的蒋应成,21岁就夺得世界技能大赛汽车喷漆项目的冠军,赢得鲜花和掌声后,蒋应成婉拒了不少企业的高薪聘请,选择留在母校当一名老师。1818黄金眼21岁拿下世界冠军留校继五十米外打贴纸狙击冠军耐得住柳欣波是警队的一名狙击手,今年在杭州市特警狙击手排位赛当中拿下了季度冠军。他说要想成为射击高手,不仅要耐得住寂寞,还要不断挑战自己的极限。1818黄金眼五十米外打贴纸狙击冠军耐得住官方维拉主帅杰拉德下课球队11轮9分倒数第四直播吧10月21日讯维拉本轮客场03负于富勒姆,赛后,维拉官方宣布主帅杰拉德下课。维拉官方公告我们要感谢杰拉德的辛勤工作,并祝愿他未来一切顺利。维拉在今夏连番引进强援,但至今表现未能源危机持续发酵,带热中国高领毛衣在欧洲畅销搜索量大涨13倍,马克龙带货,中国高领毛衣在欧洲火了。虽然2022巴黎秋冬时装周刚闭幕不久,但对欧洲人来说,今年冬天男装的最火单品已提前锁定高领毛衣。在法国经济部长总统先后穿着高领轰244,一波打崩马布里!吴前1511大发神威,浙江6连胜露冠军相CBA新赛季打到现在,表现最好的队伍,非浙江队莫属!新赛季至今,浙江队5战全胜,接连击败了宁波天津上海同曦新疆5支球队,保持全胜的同时,净胜分更是高达129分,场均赢将近26分,真
实体凉透了?上半年净消失4700家门店,数字化转型箭在弦上2022,实体零售依旧艰难2022年,可能是未来5年最难的一年!零售行业凛冬将至!你可别不信,仅仅上半年,就有4700家实体店关闭,比如美特斯邦威关店351家!森马关店860家!最看铁塔凌云古银杏广东韶关南雄的坪田是观赏千年古银杏的好地方。在银杏树叶渐渐由翠绿变成金黄色的时候,我携老伴来到了坪田的景点。眼前一棵树身黑悠悠的千年古银杏铁塔凌云,称之为中国最美古树据说银杏原产中初冬的沈阳,真美!冬雪后,到处藏着姹紫嫣红与生机勃勃刚刚过去的周末,落叶与冬雪邂逅,让整个沈阳城都笼罩在浪漫中。虽然室外寒风瑟瑟,雪后还是有很多市民出门打卡,记录下被白雪覆盖的城市景色。那些街上玩乐的大人孩子们,为生活奔波在路上的外吃土豆是否会增加罹患心脏代谢疾病的风险?最新研究带来不同意见最近的研究表明,总体饮食和生活方式,而不是具体的马铃薯烹饪技术,影响着与马铃薯消费有关的健康结果。尽管马铃薯是一种充满营养的蔬菜,但它经常被挑出来作为一种需要限制的食物。事实上,一您知道五红汤是什么吗?有哪些功效呢?不妨来了解一下你知道五红汤是什么吗?其实五红汤的原料是生活中很常见的五种食材,红枣,红豆,红皮花生,红糖和枸杞。因为这五种食物都是红色,所以叫做五红汤。五红汤中,红枣有补血的作用,红衣花生能养血中医方法暖五脏,补足阳气好过冬入冬后,各地气候相继寒冷起来,五脏阳气充足才能发挥正常功能,中医有不少暖五脏的好方法。温阳补心防心病对于容易心慌肢冷畏寒失眠等阴寒体质的人群,日常不妨试试温阳补心的方法,有助预防心癌症患者最应该吃的一类蔬菜,原来是它阅读前请点关注,每天定时分享关于乳腺肿瘤及癌症知识,拥抱每一位肿瘤患者,让你在抗癌的路上不孤单蔬菜是均衡膳食的重要组成部分。患癌后,我们对营养的需求变得更为迫切,对食物的选择也变得提醒中老年人进入冬季,若有条件,5种营养食物常吃,平稳度冬冬季进补,来年打虎,这句俗语之所以代代流传,是因为它具有一定的合理性。特别是对于中老年人而言,相较于舒适的春季和秋季,以及炎热的夏季,冬季带来的寒风和低温给他们的日常生活带来不小的船网大战疲卡休NBA常规赛船网大战,篮网11095狂胜快船,喜提二连胜。老杜库里合砍50分,乔治尽力了,队友是缅北之王,拿钱就跑。伦纳德狂砍0分0板1失踪,伦纳德都打一战了,歇一辈子怎么了。完全狂野东部!凯尔特人反超雄鹿领跑,76人热火反弹,篮网仅第1211月15日,NBA东部的格局发生很大的变化,雄鹿再度输给老鹰,再加上凯尔特人的赢球,这样雄鹿让出东部榜首的位置,绿衫军升至第一。热火逆转击败西部豪强太阳,同时76人也实现了崛起。没好控卫只能靠内线,大王再砍20助中国男篮取两连胜,锁定世界杯名额北京时间今天凌晨,征战世预赛的中国男篮用一个并不能让人满意的过程和一个最好的结果,最终通过加时赛以8067战胜了主场作战的巴林队,提前两轮取得了参加明年男篮世界杯的名额。队长王哲林