Realm資料庫增刪查改,加密以及其他相關特性
Realm使用方法及相關特性
最近接觸到Realm資料庫,經過幾天研究感覺Realm就是為速度而生的!在保證了ACID的要求下,很多設計都是以速度為主。當然,Realm 最核心的理念就是物件驅動,這是 Realm 的核心原則。Realm 本質上是一個嵌入式資料庫,但是它也是看待資料的另一種方式。它用另一種角度來重新看待移動應用中的模型和業務邏輯。
下面是使用方法以及一些特性
1. 建立資料庫
- (void)creatDataBaseWithName:(NSString *)databaseName
{
NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES );
NSString *path = [docPath objectAtIndex:0];
NSString *filePath = [path stringByAppendingPathComponent:databaseName];
NSLog(@"資料庫目錄 = %@",filePath);
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.fileURL = [NSURL URLWithString:filePath];
config.objectClasses = @[MyClass.class, MyOtherClass.class];
config.readOnly = NO;
int currentVersion = 1.0;
config.schemaVersion = currentVersion;
config.migrationBlock = ^(RLMMigration *migration , uint64_t oldSchemaVersion) {
// 這裡是設定資料遷移的block
if (oldSchemaVersion < currentVersion) {
}
};
[RLMRealmConfiguration setDefaultConfiguration:config];
}
注:建立資料庫主要設定RLMRealmConfiguration,設定資料庫名字和儲存地方。把路徑以及資料庫名字拼接好字串,賦值給fileURL即可。
2. 記憶體資料庫
通常情況下,Realm 資料庫是儲存在硬碟中的,但是您能夠通過設定inMemoryIdentifier而不是設定RLMRealmConfiguration中的 fileURL屬性,以建立一個完全在記憶體中執行的資料庫。
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];config.inMemoryIdentifier = @"MyInMemoryRealm";RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
記憶體資料庫在每次程式執行期間都不會儲存資料。但是,這不會妨礙到 Realm 的其他功能,包括查詢、關係以及執行緒安全。
如果需要一種靈活的資料讀寫但又不想儲存資料的方式的話,那麼可以選擇用記憶體資料庫。
使用記憶體資料庫需要注意的是:
記憶體資料庫會在臨時資料夾中建立多個檔案,用來協調處理諸如跨程序通知之類的事務。 實際上沒有任何的資料會被寫入到這些檔案當中,除非作業系統由於記憶體過滿需要清除磁碟上的多餘空間。
如果某個記憶體 Realm 資料庫例項沒有被引用,那麼所有的資料就會被釋放。所以必須要在應用的生命週期內保持對Realm記憶體資料庫的強引用,以避免資料丟失。
3. 建表
Realm資料模型是基於標準 Objective‑C 類來進行定義的,使用屬性來完成模型的具體定義。
我們只需要繼承 RLMObject或者一個已經存在的模型類,您就可以建立一個新的 Realm 資料模型物件。對應在資料庫裡面就是一張表。
#import <Realm/Realm.h>
@interface RLMUser : RLMObject
@property NSString *accid;
@property NSInteger custId;
@property NSString *custName;
@property NSString *avatarBig;
@property RLMArray<Car> *cars;
RLM_ARRAY_TYPE(RLMUser) // 定義RLMArray<RLMUser>
@interface Car : RLMObject
@property NSString *carName;
@property RLMUser *owner;
@end
RLM_ARRAY_TYPE(Car) // 定義RLMArray<Car>
@end
注意,RLMObject 官方建議不要加上 Objective-C的property attributes(如nonatomic, atomic, strong, copy, weak 等等)假如設定了,這些attributes會一直生效直到RLMObject被寫入realm資料庫。
4. 儲存資料
// (1) 建立一個Car物件,然後設定其屬性
Car *car = [[Car alloc] init];
car.carName = @"Lamborghini";
// (2) 通過字典建立Car物件
Car *myOtherCar = [[Car alloc] initWithValue:@{@"name" : @"Rolls-Royce"}];
// (3) 通過陣列建立狗狗物件
Car *myThirdcar = [[Car alloc] initWithValue:@[@"BMW"]];
注意,所有的必需屬性都必須在物件新增到 Realm 前被賦值
5. 增加操作
[realm beginWriteTransaction];
[realm addObject:Car];
[realm commitWriteTransaction];
請注意,如果在程序中存在多個寫入操作的話,那麼單個寫入操作將會阻塞其餘的寫入操作,並且還會鎖定該操作所在的當前執行緒。
Realm這個特性與其他持久化解決方案類似,我們建議您使用該方案常規的最佳做法:將寫入操作轉移到一個獨立的執行緒中執行。
官方給出了一個建議:
由於 Realm 採用了 MVCC 設計架構, 讀取操作並不會因為寫入事務正在進行而受到影響 。除非您需要立即使用多個執行緒來同時執行寫入操作,不然您應當採用批量化的寫入事務,而不是採用多次少量的寫入事務。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject: Car];
}];
});
上面的程式碼就是把寫事務放到子執行緒中去處理。
6. 刪除操作
[realm beginWriteTransaction];
// 刪除單條記錄
[realm deleteObject:Car];
// 刪除多條記錄
[realm deleteObjects:CarResult];
// 刪除所有記錄
[realm deleteAllObjects];
[realm commitWriteTransaction];
7. 修改操作
當沒有主鍵的情況下,需要先查詢,再修改資料。 當有主鍵的情況下,有以下幾個非常好用的API
[realm addOrUpdateObject:Car];
[Car createOrUpdateInRealm:realm withValue:@{@"id": @1, @"price": @9000.0f}];
addOrUpdateObject會去先查詢有沒有傳入的Car相同的主鍵,如果有,就更新該條資料。這裡需要注意, addOrUpdateObject這個方法不是增量更新 ,所有的值都必須有,如果有哪幾個值是null,那麼就會覆蓋原來已經有的值,這樣就會出現資料丟失的問題。
createOrUpdateInRealm:withValue:這個方法是增量更新的,後面傳一個字典,使用這個方法的前提是有主鍵。方法會先去主鍵裡面找有沒有字典裡面傳入的主鍵的記錄,如果有,就只更新字典裡面的子集。如果沒有,就新建一條記錄。
8. 查詢操作
在Realm中所有的查詢(包括查詢和屬性訪問)在 Realm 中都是延遲載入的,只有當屬性被訪問時,才能夠讀取相應的資料。
查詢結果並不是資料的拷貝:修改查詢結果(在寫入事務中)會直接修改硬碟上的資料。同樣地,您可以直接通過包含在RLMResults 中的RLMObject物件完成遍歷關係圖的操作。除非查詢結果被使用,否則檢索的執行將會被推遲。這意味著連結幾個不同的臨時 {RLMResults } 來進行排序和匹配資料,不會執行額外的工作,例如處理中間狀態。 一旦檢索執行之後,或者通知模組被新增之後, RLMResults將隨時保持更新,接收 Realm 中,在後臺執行緒上執行的檢索操作中可能所做的更改。
//從預設資料庫查詢所有的車
RLMResults<Car *> *cars = [Car allObjects];
// 使用斷言字串查詢
RLMResults<Dog *> *tanDogs = [Dog objectsWhere:@"color = '棕黃色' AND name BEGINSWITH '大'"];
// 使用 NSPredicate 查詢
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@",@"棕黃色", @"大"];
RLMResults *results = [Dog objectsWithPredicate:pred];
// 排序名字以“大”開頭的棕黃色狗狗
RLMResults<Dog *> *sortedDogs = [[Dog objectsWhere:@"color = '棕黃色' AND name BEGINSWITH '大'"] sortedResultsUsingProperty:@"name" ascending:YES];
Realm鏈式查詢
Realm 查詢引擎一個特性就是它能夠通過非常小的事務開銷來執行鏈式查詢(chain queries),而不需要像傳統資料庫那樣為每個成功的查詢建立一個不同的資料庫伺服器訪問。
RLMResults<Car *> *Cars = [Car objectsWhere:@"color = blue"];
RLMResults<Car *> *CarsWithBNames = [Cars objectsWhere:@"name BEGINSWITH 'B'"];
9. Realm支援資料庫加密
Realm封裝好了加密,如果有隱私資料可以直接加密,目前我這邊暫時沒有用到加密.
// 產生隨機金鑰
NSMutableData *key = [NSMutableData dataWithLength:64];
SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes);
// 開啟加密檔案
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.encryptionKey = key;
NSError *error = nil;
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (!realm) {
// 如果金鑰錯誤,`error` 會提示資料庫不可訪問
NSLog(@"Error opening realm: %@", error);
}
Realm 支援在建立 Realm 資料庫時採用64位的金鑰對資料庫檔案進行 AES-256+SHA2 加密。這樣硬碟上的資料都能都採用AES-256來進行加密和解密,並用 SHA-2 HMAC 來進行驗證。每次您要獲取一個 Realm 例項時,您都需要提供一次相同的金鑰。
不過,加密過的 Realm 只會帶來很少的額外資源佔用(通常最多隻會比平常慢10%)。
10. Realm KVC和KVO特性
RLMObject、RLMResult以及 RLMArray
都遵守鍵值編碼(Key-Value Coding)(KVC)機制。當您在執行時才能決定哪個屬性需要更新的時候,這個方法是最有用的。 將 KVC 應用在集合當中是大量更新物件的極佳方式,這樣就可以不用經常遍歷集合,為每個專案建立一個訪問器了。
RLMResults<Person *> *persons = [Person allObjects];
[[RLMRealm defaultRealm] transactionWithBlock:^{
[[persons firstObject] setValue:@YES forKeyPath:@"isFirst"]; // 將每個人的 planet 屬性設定為“地球”
[persons setValue:@"地球" forKeyPath:@"planet"];
}];
Realm 物件的大多數屬性都遵從 KVO 機制。所有 RLMObject子類的持久化(persisted)儲存(未被忽略)的屬性都是遵循 KVO 機制的,並且 RLMObject以及 RLMArray中 無效的(invalidated)屬性也同樣遵循(然而 RLMLinkingObjects屬性並不能使用 KVO 進行觀察)。