關於iOS GYDataCenter本地資料庫解決方案的那些事兒--下卷
阿新 • • 發佈:2019-01-03
之前的部落格講解了如何儲存使用者的敏感資訊和如何使用GYDataCenter建立資料庫和資料表,這一篇主要談一談在開發中經常用的增刪查改解決方案。
3.如何進行增刪查改
先來檢視一下GY的標頭檔案,在GYDataCenter.h裡面只有兩個標頭檔案。
這裡我們就知道了,外部主要使用的就只有GYDataContent和GYModelObject這兩個類的內容,上一次在建立表格的時候就是繼承了GYModelObject,從字面上理解,我們的資料庫操作就應該屬於GYDataContext的內容了。
#import <Foundation/Foundation.h> #import "GYDBRunner.h" @protocol GYModelObjectProtocol; @interface GYDataContext : NSObject + (GYDataContext *)sharedInstance; /** * * @param modelClass Class of the model object that you want to fetch. * 需要查詢的資料型別,在這裡就是需要操作的資料表 * @param properties Properties you need. Pass nil to get values of all properties. * 需要查詢的表格中的欄位,如果傳入空的Array,返回所有的屬性 * @param primaryKey Primary key value of the model object that you want to fetch. * 需要查詢的資料的主鍵 * @return Object that match the primary key value, or nil if none is found. * 返回傳入的表格的物件,物件內持有傳入的欄位的值 */ - (id)getObject:(Class<GYModelObjectProtocol>)modelClass properties:(NSArray *)properties primaryKey:(id)primaryKey; /** * * @param modelClass Class of the model objects that you want to fetch. * 需要查詢的資料型別,在這裡就是需要操作的資料表 * @param properties Properties you need. Pass nil to get values of all properties. * 需要查詢的表格中的欄位,如果傳入空的Array,返回所有的屬性 * @param where Where clause of SQL. Use '?'s as placeholders for arguments. * 這裡傳入SQL語句,格式為“where........” 這裡是SQL中的條件語句 * @param arguments Values to bind to the where clause. * 上述SQL語句對應的Value * @return Objects that match the condition of the where clause. * 返回傳入的表格的物件,物件內持有傳入的欄位的值 */ - (NSArray *)getObjects:(Class<GYModelObjectProtocol>)modelClass properties:(NSArray *)properties where:(NSString *)where arguments:(NSArray *)arguments; /** Join two tables. * 連標查詢 * @param leftClass Class of the first join table. * 第一個關聯的表格,一般為父表 * @param leftProperties Properties of leftClass that you need. Pass nil to get values of all properties. * 第一個表格的欄位,一般為父表的欄位,如果傳入空的Array,返回所有的屬性 * @param rightClass Class of the second join table. * 關聯的第二個表格,一般為子表 * @param rightProperties Properties of rightClass that you need. Pass nil to get values of all properties. * 關聯的字表的欄位,一般為字表的欄位, 如果傳入空的Array,返回所有的屬性 * @param joinType GYSQLJoinTypeInner, GYSQLJoinTypeLeft or GYSQLJoinTypeCross. * 兩個表關聯的方式 * @param joinCondition Join condition. For example: 'leftTableName.property1 = rightTableName.property2'. * 關聯的欄位 * @param where Where clause of SQL. Use '?'s as placeholders for arguments. * 這裡傳入SQL語句,格式為“where........” 這裡是SQL中的條件語句 * @param arguments Values to bind to the where clause. * 上述SQL語句對應的Value * @return @[ @[`Objects of left class`], @[`objects of right class`] ]. * 返回傳入的表格的物件的陣列,物件內持有傳入的欄位的值 */ - (NSArray *)getObjects:(Class<GYModelObjectProtocol>)leftClass properties:(NSArray *)leftProperties objects:(Class<GYModelObjectProtocol>)rightClass properties:(NSArray *)rightProperties joinType:(GYSQLJoinType)joinType joinCondition:(NSString *)joinCondition where:(NSString *)where arguments:(NSArray *)arguments; /** * * @param modelClass Class of the model objects that you want to query. * 需要查詢的資料型別,在這裡就是需要操作的資料表 * @param where Where clause of SQL. Use '?'s as placeholders for arguments. * 這裡傳入SQL語句,格式為“where........” 這裡是SQL中的條件語句 * @param arguments Values to bind to the where clause. * 上述SQL語句對應的Value * @return Primary key values that match the condition of the where clause. * 返回一組表物件 */ - (NSArray *)getIds:(Class<GYModelObjectProtocol>)modelClass where:(NSString *)where arguments:(NSArray *)arguments; /** * * @param modelClass Class of the model objects that you want to query. * 需要查詢的資料型別,在這裡就是需要操作的資料表 * @param function Aggregate function. For example: 'count(*)', 'sum(value)'... * 用於來統計 * @param where Where clause of SQL. Use '?'s as placeholders for arguments. * 這裡傳入SQL語句,格式為“where........” 這裡是SQL中的條件語句 * @param arguments Values to bind to the where clause. * 上述SQL語句對應的Value * @return Result of the aggregate function. * 返回統計結果 */ - (NSNumber *)aggregate:(Class<GYModelObjectProtocol>)modelClass function:(NSString *)function where:(NSString *)where arguments:(NSArray *)arguments; /** * * @param object The object to be saved. * 儲存表資料 */ - (void)saveObject:(id<GYModelObjectProtocol>)object; /** *清除某張表的所有資料 * */ - (void)deleteObject:(Class<GYModelObjectProtocol>)modelClass primaryKey:(id)primartyKey; /** * * @param modelClass Class of the model objects that you want to delete. * 需要刪除資料表格 * @param where Where clause of SQL. Use '?'s as placeholders for arguments. * 需要刪除的條件 * @param arguments Values to bind to the where clause. * 需要刪除條件的值 */ - (void)deleteObjects:(Class<GYModelObjectProtocol>)modelClass where:(NSString *)where arguments:(NSArray *)arguments; /** * * @param modelClass Class of the model object that you want to update. * 需要更新的表 * @param set Property and new value pairs. * 需要更新的內容,這裡傳入一個字典 * @param primaryKey Primary key value of the model object that you want to update. * 主鍵 */ - (void)updateObject:(Class<GYModelObjectProtocol>)modelClass set:(NSDictionary *)set primaryKey:(id)primaryKey; /** * * @param modelClass Class of the model object that you want to update. * 需要更新的表 * @param set Property and new value pairs. * 需要更新的內容,這裡傳入一個字典 * @param primaryKey Primary key value of the model object that you want to update. * 主鍵 * @return A new updated object. * 返回更新後的物件 */ - (id)updateAndReturnObject:(Class<GYModelObjectProtocol>)modelClass set:(NSDictionary *)set primaryKey:(id)primaryKey; /** * * @param modelClass Class of the model objects that you want to update. * 需要更新的表 * @param set Property and new value pairs. * 需要更新的內容,這裡傳入一個字典 * @param where Where clause of SQL. Use '?'s as placeholders for arguments. * 需要更新的SQL,這裡是條件語句 * @param arguments Values to bind to the where clause. * SQL語句對應的值 */ - (void)updateObjects:(Class<GYModelObjectProtocol>)modelClass set:(NSDictionary *)set where:(NSString *)where arguments:(NSArray *)arguments; //建立一個數據庫事務 - (void)inTransaction:(dispatch_block_t)block dbName:(NSString *)dbName; - (void)vacuumAllDBs; - (void)synchronizeAllData; @end
現在上面的方法進行逐一講解: 這裡我們需要理解的一點,我們不需要直接呼叫上面的方法,在我們建立資料表的時候,那一個表模型就封裝了一層,直接呼叫模型表的方法就行了。
a.增刪查改--查,查詢在這個資料庫中是很重要的。
/* @method queryAllContactsInfos @abstrac 查詢所有聯絡人資訊 @discussion 查詢所有聯絡人資訊 @param No param @result @[QYJContactsInfo, ...] */ + (NSArray *)queryAllContactsInfos { NSMutableArray *results = nil; NSString *where = @"";//這裡查詢所有的標中的資料,不需要額外的條件,故傳null NSArray *arguments = nil;//上面SQL是NULL,所以這裡傳入一個nil,sql的中的'?'佔位符與陣列的個數對應 /** * 此處如果需要對查獲的資料進行出庫的時候的排序可以使用ORDER BY (order by) * ASC 為升序, DESC 為降序, 不寫預設為升序,不使用order by按自增ID排序輸出 * e.g: * NSString *where = @"ORDER BY name";//按名字升序 * NSString *where = @"ORDER BY name, phoneNum DESC"//先按名字升序,再按電話號碼降序 * NSString *where = @"ORDER BY name DESC, phoneNum"//先按名字降序,再按電話號碼升序 * 其他查詢的寫法和SQL語句一致 * "where name = ? ORDER BY nameFirstLetter" * "where name = ? AND phoneNum ORDER BY name"; */ results = [QYJContactsInfo objectsWhere:where arguments:arguments].mutableCopy; return results; } /* @method queryContactsInfoByName: @abstrac 根據姓名去查詢聯絡人 @discussion 根據姓名去查詢聯絡人 @param name NSString @result @[QYJContactsInfo, ...] */ + (NSArray *)queryContactsInfoByName:(NSString *)name { //手機聯絡人可能存在同名的 NSMutableArray *results = nil; /** * 如果需要多個查詢的條件用AND來連線 * e.g: 根據姓名和號碼查詢 * NSString *where = @"WHERE name = ? AND phoneNum = ?"; * NSArray *arguments = @[name, phoneNum]; * * 如果需要模糊查詢用%%來表示,這OC中%需要轉譯 * e.g: * NSString *where = @"WHERE name LIKE '%%?%%'"; * NSArray *arguments = @[name]; * OR * NSString *where = [NSString stringWithFormat:@"WHERE name LIKE '%%%@%%'", name]; * * 如果需要滿足任意條件的資料使用OR來連線 * e.g: * NSString *where = @"WHERE name = ? OR phoneNum = ?" * NSArray *arguments = @[name, phoneNum]; */ NSString *where = @"WHERE name = ?"; NSArray *arguments = @[name]; results = [QYJContactsInfo objectsWhere:where arguments:arguments].mutableCopy; return results; }
b.增刪查改--改,改就是更新,這裡的原則是能在庫裡面修改的資料,就不要拿出資料庫來。
/* @method updateContactsInfoByName:set: @abstrac 按名字修改對應的欄位的資料 @discussion 按名字修改對應的欄位的資料 @param name NSString set NSDictionary @result No result */ + (void)updateContactsInfoByName:(NSString *)name set:(NSDictionary *)set { /** * 假定 name為Avalanching, set 是 @{@"phoneNum":@"11111111"}; * 將資料表中name為Avalanching 的 phoneNum 的值改為 "11111111" */ NSString *where = @"WHERE name = ?"; NSArray *arguments = @[name]; //不會返回更新後的資料 [QYJContactsInfo updateObjectsSet:set Where:where arguments:arguments]; } /* @method updateContactsInfoByInfo:set: @abstrac 將需要修改的QYJContactsInfo物件和修改的資料傳入,更新表資料 @discussion 將需要修改的QYJContactsInfo物件和修改的資料傳入,更新表資料 @param info QYJContactsInfo set NSDictionary @result No result */ + (void)updateContactsInfoByInfo:(QYJContactsInfo *)info set:(NSDictionary *)set { //這裡直接用到GYDataContext的方法,要引入GYDataContext.h //這裡是更新QYJContactsInfo中 primaryKeyId 等於 info.primaryKeyId 的資料 [[GYDataContext sharedInstance] updateObject:[info class] set:set primaryKey:@(info.primaryKeyId)]; //這裡是更新QYJContactsInfo中 primaryKeyId 等於 info.primaryKeyId 的資料 並返回新的物件 QYJContactsInfo *newinfo = [[GYDataContext sharedInstance] updateAndReturnObject:[info class] set:set primaryKey:@(info.primaryKeyId)]; //將表中滿足條件的資料全部更新成set中的數值 NSString *where = @"WHERE name = ?"; NSArray *arguments = @[info.name]; [[GYDataContext sharedInstance] updateObjects:[info class] set:set where:where arguments:arguments]; }
c.增刪查改--刪,刪除(邏輯刪除和物理刪除)
這裡首先要明確,邏輯刪除不是刪除,僅僅是將資料庫中的資料狀態改變一下,邏輯刪除根據資料庫結構來定的,這裡不做講解;物理刪除就將資料徹底從資料庫中移除。/*
@method cleanContactsInfos
@abstrac 清空通訊錄表格裡面所有的資料
@discussion 清空通訊錄表格裡面所有的資料
@param No param
@result No result
*/
+ (void)cleanContactsInfos {
NSString *where = @""; //清除所有資料,不填寫任何條件
NSArray *arguments = nil;
/**
* 按條件刪除
* where = @"WHERE name = ?";
* argument = @[name];
*/
[[GYDataContext sharedInstance] deleteObjects:[QYJContactsInfo class] where:where arguments:arguments];
//根據primaryKeyId去刪除資料
[[GYDataContext sharedInstance] deleteObject:[QYJContactsInfo class] primaryKey:@(1)];
}
/*
@method deleteContactsInfosByInfo:
@abstrac 刪除某一條資料
@discussion 刪除資料
@param info QYJContactsInfo
@result No result
*/
+ (void)deleteContactsInfosByInfo:(QYJContactsInfo *)info {
[info deleteObject];
}
d.其他,開發中經常用到一些問題。
1.特殊字元的轉譯查詢的問題,'%', ' ' ' , '/'等等。
/*
@method queryContactsInfoBySpecialCharacter:
@abstrac 查詢包含特殊字元的資料 (_, %, /, \, *)
@discussion 查詢包含特殊字元的資料 (_, %, /, \, *)
@param character 特殊字元
@result NSArray<QYJContactsInfo *> *
*/
+ (NSArray<QYJContactsInfo *> *)queryContactsInfoBySpecialCharacter:(NSString *)character {
//使用escape關鍵字和[]來轉譯查詢 /, *, %這一類的關鍵字
//NSString *where = [NSString stringWithFormat:@"WHERE name LIKE '[%@]'", character];
NSString *where = [NSString stringWithFormat:@"WHERE name LIKE '%%*%@%%' escape'*' ORDER BY namePinyin", character];
NSArray *arguments = nil;
return [QYJContactsInfo objectsWhere:where arguments:arguments];
}
2.連結串列查詢
/*
@method associationQuery
@abstrac 關聯查詢
@discussion 關聯查詢
@param No param
@result No result
*/
+ (NSArray *)associationQuery {
NSString *where = @"";
NSArray *arguments = @[];
//這裡只建立了一張表,所以兩個表都寫為[QYJContactsInfo class], 實際開發是兩張不同的表格
NSArray *result = [[GYDataContext sharedInstance] getObjects:[QYJContactsInfo class]
properties:nil
objects:[QYJContactsInfo class]
properties:nil
joinType:GYSQLJoinTypeInner
joinCondition:@"這裡是相關聯的欄位"
where:where
arguments:arguments];
//如果不能理解這查詢的方式,可以先查一張表,再把查處來的表格,再查另外一張表
return result;
}
3.索引表的快速刪除
#define Local_Tabel @"IndexesTabel"
#define dbPath @"這裡是資料庫的路徑"
#define local_dbQueue [FMDatabaseQueue databaseQueueWithPath:dbPath]
+ (void)updateIndexesTabelByCondition:(NSString *)condition {
//這裡需要直接寫SQL,所以這裡需要用底層的FMDB
// 一般索引表都是存一個id, ids 欄位 對應的只為"1, 2, 3, 4, 5, 6";
// id 為4 的資料已經從主表中刪除了,現在要修改ids含有4的資料,將4去除
// 才用replace關鍵字去替代,一次替代之後有可能出現的情況是
// "1, 2, 3, 4, 5" =(第一次replace)=> "1, 2, 3, ,5"
// "4, 5, 6" =(第一次replace)=> ",5,6"
// "1, 2, 3, 4" =(第一次replace)=> "1, 2, 3,"
//後面兩條SQL 用於修改正 資料庫裡面的結構
NSString * sql = [NSString stringWithFormat:@"UPDATE %@ SET schedule_id = REPLACE(schedule_id, ?, '')", Local_Tabel];
NSString * sqlDetele = [NSString stringWithFormat:@"DELETE FROM %@ WHERE schedule_id = ','", Local_Tabel];
NSString * sqlRepace = [NSString stringWithFormat:@"UPDATE %@ SET schedule_id = REPLACE(schedule_id, ',,', ',')", Local_Tabel];
[local_dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:sql, condition];
[db executeUpdate:sqlRepace];
[db executeUpdate:sqlDetele];
}];
}
4.資料遷移 這裡不提供具體程式碼,只提供思路,最簡單的做法就是從將舊的資料從資料庫中取出來,然後組建成新的資料格式存入新的表格,這裡我們上次提到了一個重寫 - ( BOOL )isEqual:( id )object 的方法,這裡可以通過NSArray轉NSSet的方式過濾掉重複的資料,具體判斷相等的規則就又開發者根據具體的情況去定了。 一般常規開發中升級資料庫,需要將舊的表格的資料取出來,然後建立新表格去存放,小版本的升級就要避免遷移的時候修改很多,這裡就要讓舊錶和新表名字保持一致,將操作放到一個事務裡面去處理。
這些就是簡單的GYDataCenter資料庫使用了。