前言數據庫
FMDB庫雖然進一步對封裝了系統自己的SQLite操做邏輯,可是使用過的朋友都知道,你仍是須要寫一堆的SQL操做語句才能進行操做並且並不支持實體操做。爲了解決這些問題就須要對FMDB進行二次封裝,支持實體操做以及簡化其操做邏輯。架構
大致的思路;框架
(1)經過runtime獲取model實體的屬性變量名充當數據表的字段名,屬性變量類型充當數據表字段類型,實體的類名充當數據表的名稱;atom
(2)封裝基本的數據庫操做邏輯(增刪改查),經過拼接SQL字符串,而後採用FMDB第三方庫操做SQL語句。設計
總體設計架構主要的兩個部分,數據庫建立管理和數據表操做管理;版本控制
(1)統一管理的服務類DBService,擁有數據庫管理對象,提供操做數據庫的外部接口,相似外觀模式。code
(2)數據庫管理,經過JSDatabase基類實現數據庫建立以及註冊數據表的生成,通常使用方法是經過子類繼承該類配置數據庫存儲路徑,並擁有管理全部表對象的權限;對象
(3)數據表操做管理,經過JSContentTable基類實現全部的邏輯,每個子類繼承該類對應一個操做數據表的對象,這些表管理對象由數據庫管理對象管理(也就是繼承JSDatabase的子類);繼承
設計思路參照.Net的EF框架的設計思路,表對象對應數據表,而DBService就至關於EF的DataContext數據庫上下文管理對象,經過DBService能夠完成整個數據庫全部表的CURD操做。索引
數據庫管理基類,負責建立數據庫以及建立全部數據表和建立版本控制表進行數據庫版本升級,而且經過子類設置數據庫存放路徑建立數據庫,並執行createAllTable生成數據表。
#import "JSDatabase.h" #import "FMDatabase.h" #import "FMDatabaseQueue.h" @interface JSDatabase () @property (nonatomic, copy) NSMutableArray *tables; @property (nonatomic, strong) FMDatabaseQueue *dbQueue; @property (nonatomic, copy) NSString *dbPath; @end @implementation JSDatabase - (void)dealloc { [self.dbQueue close]; } // 設置數據庫版本 - (NSInteger)getDBVersion { return 1; } // 建立數據庫並生成全部數據表; - (void)createPath:(NSString *)dbPath mode:(JsDatabaseMode)mode registerTable:(void (^)())registerTable { self.dbPath = dbPath; if (mode == JsDatabaseModeRead) { self.dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath flags:SQLITE_OPEN_READONLY]; } else if (mode == JsDatabaseModeWrite) { // FMDatabaseQueue經過gcd建立數據庫; self.dbQueue = [[FMDatabaseQueue alloc] initWithPath:dbPath]; } if (registerTable) { registerTable(); } // 生成全部數據表; [self.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) { NSString *version = [self getVersion:db]; if (!version) { [self createDBVersionTable:db]; [self createAllTable:db]; [self updateDBVersion:db]; } else if ([version integerValue] != [self getDBVersion]) { [self updateDB:db]; [self updateDBVersion:db]; } }]; } // 建立數據表; - (void)createAllTable:(FMDatabase *)aDB { [self.tables enumerateObjectsUsingBlock:^(JSContentTable * _Nonnull objTable, NSUInteger idx, BOOL * _Nonnull stop) { [objTable createTable:aDB]; }]; } /* 數據庫版本控制,經過jsdb_version管理,當數據表結構發生變化時,經過更新getDBVersion中的版本號值,執行updateDB方法進行數據表的數據遷移工做,遷移完成後再執行updateDBVersion更新jsdb_version表中的version字段的值,表示數據庫遷移成功。 */ // 取得jsdb_version數據表中最新的版本 - (NSString *)getVersion:(FMDatabase *)aDB { NSString *version = nil; FMResultSet *resultSet = [aDB executeQuery:@"select version from jsdb_version where name = 'version'"]; while ([resultSet next]) { version = [resultSet stringForColumnIndex:0]; } [resultSet close]; return version; } // 更新數據庫版本爲當前最新版本 - (void)updateDBVersion:(FMDatabase *)aDB { NSString *version = [self getVersion:aDB]; if (version) { [aDB executeUpdate:@"update jsdb_version set version = ? where name = 'version'", @([self getDBVersion])]; } else { [aDB executeUpdate:@"insert into jsdb_version (name, version) values (?,?)", @"version", @([self getDBVersion])]; } } // 剛生成數據庫時,建立數據庫版本控制表jsdb_version - (void)createDBVersionTable:(FMDatabase *)aDB { [aDB executeUpdate:@"create table if not exists jsdb_version (version vachar (20) ,name varchar (10))"]; } // 數據庫版本發生更新時,更新全部表的結構並遷移舊數據 - (void)updateDB:(FMDatabase *)aDB { [self.tables enumerateObjectsUsingBlock:^(JSContentTable * _Nonnull objTable, NSUInteger idx, BOOL * _Nonnull stop) { [objTable updateDB:aDB]; }]; } // 註冊數據表; - (JSContentTable *)registerTableClass:(Class)tableClass { JSContentTable *table = [[tableClass alloc] init]; [table configTableName]; table.dbQueue = self.dbQueue; [self.tables addObject:table]; return table; } // 保存數據表對象 - (NSMutableArray *)tables { if (!_tables) { _tables = [[NSMutableArray alloc] init]; } return _tables; } @end
數據庫子類,繼承JSDatabase,負責配置數據庫存放的路徑以及管理全部的數據表;
#import "JSDatabase.h" #import "GradeTable.h" @interface DataClient : JSDatabase @property (nonatomic, strong, readonly) GradeTable *gradeTable; + (instancetype)database; - (void)construct; @end #import "DataClient.h" #import "JSDatabaseConfig.h" @interface DataClient () @property (nonatomic, strong) NSString *dbPath; @property (nonatomic, strong) GradeTable *gradeTable; @end @implementation DataClient + (instancetype)database { DataClient *dataClient = [[DataClient alloc] init]; [dataClient construct]; return dataClient; } - (void)construct { [self createPath:self.dbPath mode:JsDatabaseModeWrite registerTable:^{ self.gradeTable = (GradeTable *)[self registerTableClass:[GradeTable class]]; }]; } //// 數據庫版本升級,重寫getDBVersion替換版本號 //- (NSInteger)getDBVersion { // // return 2; //} - (NSString *)dbPath { if (!_dbPath) { NSString *dbPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:[JSDatabaseConfig config].dataBaseName]; _dbPath = dbPath; } return _dbPath; } @end
對於DBService以及數據表類,代碼略;
(1)DBService主要是管理數據庫,而且提供全部操做數據表的外部接口,方便使用;
(2)數據表類主要是管理數據表,好比增刪改查,設置索引以及增長字段和刪除字段等操做。