MMDatabaseConn+Initial.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. //
  2. // MMDatabaseConn+Initial.m
  3. // Pods
  4. //
  5. // Created by arons on 2016/12/22.
  6. //
  7. //
  8. #import "MMDatabaseConn+Initial.h"
  9. #import <pthread.h>
  10. #import "MMDraftModel.h"
  11. #define kDBCache @"DBCache"
  12. #define kDBVERSION @"DBVersion"
  13. /** 当前使用的数据库版本,程序会根据版本号的改变升级数据库以及迁移旧的数据 */
  14. static NSString* DB_Version = @"1.0.1";
  15. /** 数据库文件名称 */
  16. static NSString* DB_NAME = @"DB.sqlite";
  17. @implementation MMDatabaseConn (Initial)
  18. #pragma mark - ......::::::: public :::::::......
  19. /**
  20. 初始化数据库,如果使用APPGroup需要传入APPGroup的URL,否则传入空
  21. 默认把数据库文件保存在Cache目录下
  22. */
  23. -(void)initDBWithAppGroupURL:(NSURL *)appGroupURL {
  24. NSURL* emojiDBURL = nil;
  25. if (appGroupURL) {
  26. emojiDBURL = [appGroupURL URLByAppendingPathComponent:DB_NAME];
  27. } else {
  28. NSString *DBPath = [self createDirectory:kDBCache];
  29. emojiDBURL = [NSURL fileURLWithPath:[DBPath stringByAppendingPathComponent:DB_NAME]];
  30. }
  31. self.DBFilePath = [emojiDBURL path];
  32. [[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey] ofItemAtPath:self.DBFilePath error:NULL];
  33. // 数据库版本控制
  34. [self dbVersionControl];
  35. // 拷贝数据库文件
  36. [self copyDefaultDBResources];
  37. // 在子线程中初始化数据表
  38. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  39. // 数据库表初始化
  40. [self initTables];
  41. });
  42. }
  43. // 创建文件夹
  44. - (NSString *)createDirectory:(NSString *)path {
  45. BOOL isDir = NO;
  46. NSString* cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  47. NSString *finalPath = [cachePath stringByAppendingPathComponent:path];
  48. if (!([[NSFileManager defaultManager] fileExistsAtPath:finalPath isDirectory:&isDir]
  49. && isDir)) {
  50. [[NSFileManager defaultManager] createDirectoryAtPath:finalPath
  51. withIntermediateDirectories :YES
  52. attributes :nil
  53. error :nil];
  54. }
  55. return finalPath;
  56. }
  57. /**
  58. 初始化默认数据
  59. */
  60. - (void)initDefaultDatas {
  61. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  62. });
  63. }
  64. #pragma mark - ......::::::: private :::::::......
  65. - (void)copyDefaultDBResources {
  66. // FIXME: ZYT 拷贝默认的数据库资源
  67. NSString* sourceFilePath = @"";
  68. NSString* destDBFilePath = @"";
  69. if (![[NSFileManager defaultManager] fileExistsAtPath:destDBFilePath]) {
  70. BOOL isSourceFileExist = [[NSFileManager defaultManager] fileExistsAtPath:sourceFilePath];
  71. if (isSourceFileExist) {
  72. NSError* err;
  73. [[NSFileManager defaultManager] copyItemAtPath:sourceFilePath toPath:destDBFilePath error:&err];
  74. }
  75. }
  76. }
  77. /**
  78. * 初始化数据表
  79. */
  80. -(void)initTables{
  81. [MMDraftModel createTableIfNotExists];
  82. }
  83. #pragma mark - ......::::::: upgrade :::::::......
  84. /**
  85. * 数据库版本控制
  86. * 版本号保存在DB_Version宏定义中
  87. */
  88. - (void)dbVersionControl {
  89. // 基础数据库版本管理
  90. [self baseDBVersionControl];
  91. }
  92. // 创建新的临时表,把数据导入临时表,然后用临时表替换原表
  93. - (void)baseDBVersionControl {
  94. NSString * version_old = [[NSUserDefaults standardUserDefaults] stringForKey:kDBVERSION];
  95. NSString * version_new = [NSString stringWithFormat:@"%@", DB_Version];
  96. NSLog(@"dbVersionControl before: %@ after: %@",version_old,version_new);
  97. // 数据库版本升级
  98. if (version_old != nil && ![version_new isEqualToString:version_old]) {
  99. // 获取数据库中旧的表
  100. NSArray* existsTables = [self sqliteExistsTables];
  101. NSMutableArray* tmpExistsTables = [NSMutableArray array];
  102. // 修改表名,添加后缀“_bak”,把旧的表当做备份表
  103. for (NSString* tablename in existsTables) {
  104. [tmpExistsTables addObject:[NSString stringWithFormat:@"%@_bak", tablename]];
  105. [self.databaseQueue inDatabase:^(FMDatabase *db) {
  106. NSString* sql = [NSString stringWithFormat:@"ALTER TABLE %@ RENAME TO %@_bak", tablename, tablename];
  107. [db executeUpdate:sql];
  108. }];
  109. }
  110. existsTables = tmpExistsTables;
  111. // 创建新的表
  112. [self initTables];
  113. // 获取新创建的表
  114. NSArray* newAddedTables = [self sqliteNewAddedTables];
  115. // 遍历旧的表和新表,对比取出需要迁移的表的字段
  116. NSDictionary* migrationInfos = [self generateMigrationInfosWithOldTables:existsTables newTables:newAddedTables];
  117. // 数据迁移处理
  118. [migrationInfos enumerateKeysAndObjectsUsingBlock:^(NSString* newTableName, NSArray* publicColumns, BOOL * _Nonnull stop) {
  119. NSMutableString* colunmsString = [NSMutableString new];
  120. for (int i = 0; i<publicColumns.count; i++) {
  121. [colunmsString appendString:publicColumns[i]];
  122. if (i != publicColumns.count-1) {
  123. [colunmsString appendString:@", "];
  124. }
  125. }
  126. NSMutableString* sql = [NSMutableString new];
  127. [sql appendString:@"INSERT INTO "];
  128. [sql appendString:newTableName];
  129. [sql appendString:@"("];
  130. [sql appendString:colunmsString];
  131. [sql appendString:@")"];
  132. [sql appendString:@" SELECT "];
  133. [sql appendString:colunmsString];
  134. [sql appendString:@" FROM "];
  135. [sql appendFormat:@"%@_bak", newTableName];
  136. [self.databaseQueue inDatabase:^(FMDatabase *db) {
  137. [db executeUpdate:sql];
  138. }];
  139. }];
  140. // 删除备份表
  141. [self.databaseQueue inDatabase:^(FMDatabase *db) {
  142. [db beginTransaction];
  143. for (NSString* oldTableName in existsTables) {
  144. NSString* sql = [NSString stringWithFormat:@"DROP TABLE IF EXISTS %@", oldTableName];
  145. [db executeUpdate:sql];
  146. }
  147. [db commit];
  148. }];
  149. [[NSUserDefaults standardUserDefaults] setObject:version_new forKey:kDBVERSION];
  150. [[NSUserDefaults standardUserDefaults] synchronize];
  151. } else {
  152. [[NSUserDefaults standardUserDefaults] setObject:version_new forKey:kDBVERSION];
  153. [[NSUserDefaults standardUserDefaults] synchronize];
  154. }
  155. }
  156. - (NSDictionary*)generateMigrationInfosWithOldTables:(NSArray*)oldTables newTables:(NSArray*)newTables {
  157. NSMutableDictionary<NSString*, NSArray* >* migrationInfos = [NSMutableDictionary dictionary];
  158. for (NSString* newTableName in newTables) {
  159. NSString* oldTableName = [NSString stringWithFormat:@"%@_bak", newTableName];
  160. if ([oldTables containsObject:oldTableName]) {
  161. // 获取表数据库字段信息
  162. NSArray* oldTableColumns = [self sqliteTableColumnsWithTableName:oldTableName];
  163. NSArray* newTableColumns = [self sqliteTableColumnsWithTableName:newTableName];
  164. NSArray* publicColumns = [self publicColumnsWithOldTableColumns:oldTableColumns newTableColumns:newTableColumns];
  165. if (publicColumns.count > 0) {
  166. [migrationInfos setObject:publicColumns forKey:newTableName];
  167. }
  168. }
  169. }
  170. return migrationInfos;
  171. }
  172. - (NSArray*)publicColumnsWithOldTableColumns:(NSArray*)oldTableColumns newTableColumns:(NSArray*)newTableColumns {
  173. NSMutableArray* publicColumns = [NSMutableArray array];
  174. for (NSString* oldTableColumn in oldTableColumns) {
  175. if ([newTableColumns containsObject:oldTableColumn]) {
  176. [publicColumns addObject:oldTableColumn];
  177. }
  178. }
  179. return publicColumns;
  180. }
  181. - (NSArray*)sqliteTableColumnsWithTableName:(NSString*)tableName {
  182. __block NSMutableArray<NSString*>* tableColumes = [NSMutableArray array];
  183. [self.databaseQueue inDatabase:^(FMDatabase *db) {
  184. NSString* sql = [NSString stringWithFormat:@"PRAGMA table_info('%@')", tableName];
  185. FMResultSet *rs = [db executeQuery:sql];
  186. while ([rs next]) {
  187. NSString* columnName = [rs stringForColumn:@"name"];
  188. [tableColumes addObject:columnName];
  189. }
  190. }];
  191. return tableColumes;
  192. }
  193. - (NSArray*)sqliteExistsTables {
  194. __block NSMutableArray<NSString*>* existsTables = [NSMutableArray array];
  195. [self.databaseQueue inDatabase:^(FMDatabase *db) {
  196. NSString* sql = @"SELECT * from sqlite_master WHERE type='table'";
  197. FMResultSet *rs = [db executeQuery:sql];
  198. while ([rs next]) {
  199. NSString* tablename = [rs stringForColumn:@"name"];
  200. [existsTables addObject:tablename];
  201. }
  202. }];
  203. return existsTables;
  204. }
  205. - (NSArray*)sqliteNewAddedTables {
  206. __block NSMutableArray<NSString*>* newAddedTables = [NSMutableArray array];
  207. [self.databaseQueue inDatabase:^(FMDatabase *db) {
  208. NSString* sql = @"SELECT * from sqlite_master WHERE type='table' AND name NOT LIKE '%_bak'";
  209. FMResultSet *rs = [db executeQuery:sql];
  210. while ([rs next]) {
  211. NSString* tablename = [rs stringForColumn:@"name"];
  212. [newAddedTables addObject:tablename];
  213. }
  214. }];
  215. return newAddedTables;
  216. }
  217. @end