// // ICRDataBaseController.m // XFFruit // // Created by Xummer on 4/5/15. // Copyright (c) 2015 Xummer. All rights reserved. // #import "ICRDataBaseController.h" #import "ICRStore.h" #import "ICRTask.h" #import "ICRPostTask.h" #import "ICRAttachment.h" #import "ICRAnnouncement.h" #import "ICRPatrolPlan.h" #import "ICRPost.h" #import "ICRQuestion.h" #import "ICRStoreResult.h" #import "ICRAnswer.h" #import "ICRAnswerDetail.h" #import "Product.h" #import "User.h" #import "Survey.h" #import "Vendor.h" #import "Warehouse.h" #import "GXFProductUnit.h" #import "Accounttitle.h" #import "UserWarehouse.h" #define ICR_DB_ERROR_PARAMETER @"Parse Error: Bad Parameter(s)" NSString * const ICRDataBaseErrorDomain = @"ICRDataBaseErrorDomain"; static NSString *ICRDataBasePath = @""; @interface ICRDataBaseController () @property (strong, nonatomic) FMDatabaseQueue *m_dbQueue; @end @implementation ICRDataBaseController #pragma mark - Class Method + (instancetype)sharedController { static ICRDataBaseController *_sharedController = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedController = [[self alloc] init]; }); return _sharedController; } + (NSString *)GetDataBasePath { if (ICRDataBasePath.length == 0) { NSString *archivePath = [IBTCommon archivePathForCurrentUser]; if (!archivePath.length) { return ICRDataBasePath; } NSString *dbName = @"db"; NSString *sqliteName = [dbName stringByAppendingPathExtension:@"sqlite"]; ICRDataBasePath = [archivePath stringByAppendingPathComponent:sqliteName]; return ICRDataBasePath; } else { return ICRDataBasePath; } } + (void)CleanUpDBPath { ICRDataBasePath = @""; } + (NSError *)ErrorWithMsg:(NSString *)nsErrorMsg code:(NSInteger)uiCode { NSDictionary *userInfo = nsErrorMsg ? @{ NSLocalizedFailureReasonErrorKey : nsErrorMsg } : nil; NSError *error = [[NSError alloc] initWithDomain:ICRDataBaseErrorDomain code:uiCode userInfo:userInfo]; return error; } #pragma mark - Life Cycle - (instancetype)init { self = [super init]; if (!self) { return nil; } NSString *nsDBPath = [[self class] GetDataBasePath]; self.m_dbQueue = [FMDatabaseQueue databaseQueueWithPath:nsDBPath]; [_m_dbQueue inDatabase:^(FMDatabase *db) { NSArray *tableNameArr = @[ [Product class],[User class],[Survey class],[Vendor class],[Warehouse class],[GXFProductUnit class],[Accounttitle class],[ICRAnnouncement class],[UserWarehouse class]]; NSMutableArray *sqlBatch = [NSMutableArray array]; NSString *sql = nil; for (id cls in tableNameArr) { sql = [cls SQLForCreateTable]; [sqlBatch safeAddObject:sql]; } NSMutableString *nsSQLBatch = [[NSMutableString alloc] initWithString:@""]; for (NSString *aSQL in sqlBatch) { [nsSQLBatch appendFormat:@"%@;", aSQL]; } BOOL res = [db executeStatements:nsSQLBatch]; if (!res) { CLog(@"error to run SQL: %@", sql); } }]; return self; } #pragma mark - Storage - (void)storageEntities:(NSArray *)arrObjects objectClass:(Class)DBObjClass deleteLocal:(BOOL)bDeleteLocal handleData:(void (^)(id ))handleDataBlock complete:(void (^)(void))complete fail:(void (^)(NSError *))fail { [self storageEntities:arrObjects objectClass:DBObjClass updateNil:YES deleteLocal:bDeleteLocal handleData:handleDataBlock complete:complete fail:fail]; } - (void)storageEntities:(NSArray *)arrObjects objectClass:(Class)DBObjClass updateNil:(BOOL)bUpdateNil deleteLocal:(BOOL)bDeleteLocal handleData:(void (^)(id ))handleDataBlock complete:(void (^)(void))complete fail:(void (^)(NSError *))fail { if (!IsArrayObject(arrObjects)) { if (fail) { fail( [[self class] ErrorWithMsg:ICR_DB_ERROR_PARAMETER code:0] ); } } NSString *nsTableName = [DBObjClass TableName]; NSString *nsSortKey = [DBObjClass PrimaryKey]; NSString *nsDBSortKey = nsSortKey; NSArray *arrSortedObjects = [arrObjects sortedArrayUsingComparator:^NSComparisonResult(id a, id b) { id first = a[ nsSortKey ]; id second = b[ nsSortKey ]; return [first compare:second]; }]; NSArray *arrIDs = [arrSortedObjects valueForKeyPath:nsSortKey]; NSMutableArray *arrModObjs = [NSMutableArray array]; for (id obj in arrObjects) { id dbObj = [DBObjClass DBObject]; [dbObj praseFromJsonDict:obj isNilLeague:bUpdateNil]; [arrModObjs addObject:dbObj]; } [_m_dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) { if (bDeleteLocal) { NSMutableString *sql = [NSMutableString stringWithFormat:@"DELETE FROM %@ ", nsTableName]; NSUInteger uiRecordCount = [arrIDs count]; if (uiRecordCount > 0) { [sql appendFormat:@"WHERE %@ NOT IN %@", nsDBSortKey, [IBTModel ValuePlaceholdersWithCount:uiRecordCount]]; } CLog(@"%@", sql); // [db executeUpdate:sql withArgumentsInArray:arrIDs]; } @autoreleasepool { NSString *nsSql = nil; NSArray *arrValues = nil; for (id dbObj in arrModObjs) { if (handleDataBlock) { handleDataBlock( dbObj ); } nsSql = [dbObj SQLForInsertOrUpdate:&arrValues]; [db executeUpdate:nsSql withArgumentsInArray:arrValues]; for (id subObj in dbObj.arrSubModels) { nsSql = [subObj SQLForInsertOrUpdate:&arrValues]; [db executeUpdate:nsSql withArgumentsInArray:arrValues]; } } } }]; if (complete) { complete(); } } - (void)storageEntity:(NSDictionary *)dictObject objectClass:(Class)DBObjClass handleData:(void (^)(id ))handleDataBlock complete:(void (^)(void))complete fail:(void (^)(NSError *))fail { if (!IsDictObject(dictObject)) { if (fail) { fail( [[self class] ErrorWithMsg:ICR_DB_ERROR_PARAMETER code:0] ); } } __block id dbObj = [DBObjClass DBObject]; [dbObj praseFromJsonDict:dictObject]; [_m_dbQueue inDatabase:^(FMDatabase *db) { if (handleDataBlock) { handleDataBlock( dbObj ); } NSString *nsSql = nil; NSArray *arrValues = nil; nsSql = [dbObj SQLForInsertOrUpdate:&arrValues]; [db executeUpdate:nsSql withArgumentsInArray:arrValues]; for (id subObj in dbObj.arrSubModels) { nsSql = [subObj SQLForInsertOrUpdate:&arrValues]; [db executeUpdate:nsSql withArgumentsInArray:arrValues]; } }]; if (complete) { complete(); } } - (void)storageDBObject:(id)dbObj handleData:(void (^)(id ))handleDataBlock complete:(void (^)(void))complete fail:(void (^)(NSError *))fail { if (!dbObj) { if (fail) { fail( [[self class] ErrorWithMsg:ICR_DB_ERROR_PARAMETER code:0] ); } } [_m_dbQueue inDatabase:^(FMDatabase *db) { if (handleDataBlock) { handleDataBlock( dbObj ); } NSString *nsSql = nil; NSArray *arrValues = nil; nsSql = [dbObj SQLForInsertOrUpdate:&arrValues]; [db executeUpdate:nsSql withArgumentsInArray:arrValues]; for (id subObj in dbObj.arrSubModels) { nsSql = [subObj SQLForInsertOrUpdate:&arrValues]; [db executeUpdate:nsSql withArgumentsInArray:arrValues]; } }]; if (complete) { complete(); } } #pragma mark - Query - (void)runFetchForClass:(Class)dbObjClass fetchBlock:(ICRDatabaseFetchBlock)fetchBlock fetchResultsBlock:(ICRDatabaseFetchResultsBlock)fetchResultBlock { NSParameterAssert(dbObjClass); NSParameterAssert(fetchBlock); NSParameterAssert(fetchResultBlock); [_m_dbQueue inDatabase:^(FMDatabase *db) { FMResultSet *rs = fetchBlock(db); if (rs == nil && db.lastError != nil) { [NSException raise:[NSString stringWithFormat:@"%@Exception", self.class] format:@"Invalid querry: %@", db.lastError]; fetchResultBlock(nil); return; } NSArray *fetchedObjects = [self databaseObjectsWithResultSet:rs class:dbObjClass]; dispatch_async(dispatch_get_main_queue(), ^{ fetchResultBlock(fetchedObjects); }); }]; } - (NSArray *)databaseObjectsWithResultSet:(FMResultSet *)resultSet class:(Class)dbObjClass { NSMutableArray *arrObjs = [NSMutableArray array]; while ([resultSet next]) { id obj = [dbObjClass objectFromFetchResult:resultSet]; if (obj) { [arrObjs addObject:obj]; } } return [arrObjs count] > 0 ? arrObjs : nil; } - (void)compatibilityCheck { [_m_dbQueue inDatabase:^(FMDatabase *db) { // [db open]; /* 2017-05-08 Warehouse 增加了iswms 和 isproductcenter两个字段 Product 增加了purMeasureUnit默认采购单位字段 */ if (![db columnExists:@"iswms" inTableWithName:@"Warehouse"]) { CLog(@"没有iswms 和 isproductcenter两个字段"); NSString *sql = @"DROP TABLE IF EXISTS 'Warehouse'"; [db executeUpdate:sql]; NSString *addTable = [Warehouse SQLForCreateTable]; [db executeUpdate:addTable]; } if (![db columnExists:@"purMeasureUnit" inTableWithName:@"Product"]) { NSString *sql = @"DROP TABLE IF EXISTS 'Product'"; [db executeUpdate:sql]; NSString *addTable = [Product SQLForCreateTable]; [db executeUpdate:addTable]; } /* 2018-05-07 Warehouse 增加了isspecial字段 */ if (![db columnExists:@"isspecial" inTableWithName:@"Warehouse"]) { CLog(@"没有isspecial字段"); NSString *sql = @"DROP TABLE IF EXISTS 'Warehouse'"; [db executeUpdate:sql]; NSString *addTable = [Warehouse SQLForCreateTable]; [db executeUpdate:addTable]; } // [db close]; }]; } @end