簡介
接觸Realm已經(jīng)有一段時(shí)間了,但是一直忙著項(xiàng)目,一直沒有時(shí)間做一下筆記。趁著項(xiàng)目閑著之際,我開始著手記錄我自己使用Realm過程遇到的坑。Realm是一個(gè)跨平臺(tái)的數(shù)據(jù)庫,就拿iOS來說,數(shù)據(jù)庫常用的幾種無非是Sqlite、FMDB、CoreData這幾種,每一種數(shù)據(jù)庫都在某些方面有著特別的優(yōu)勢(shì)。對(duì)于coreData,在每次使用的時(shí)候都要?jiǎng)?chuàng)建一大堆的代碼,個(gè)人是累覺不愛。而對(duì)于Sqlite是基于C語言的,用起來也是相對(duì)麻煩,F(xiàn)MDB是基于Sqlite的封裝,用起來還相對(duì)友好一點(diǎn),但是還是要寫一大堆的sql語句,但是無可否認(rèn)的是,在API上FMDB還是蠻簡單上手的。但是今天重點(diǎn)介紹的是Realm.
Realm是由Y Combinator孵化的創(chuàng)業(yè)團(tuán)隊(duì)開源出來的一款可以用于iOS(同樣適用于Swift&Objective-C)和Android的跨平臺(tái)移動(dòng)數(shù)據(jù)庫。目前最新版是Realm 2.0.2,支持的平臺(tái)包括Java,Objective-C,Swift,React Native,Xamarin。
安裝方法主要有四種(在這就不做說明)
1.Dynamic Framework(動(dòng)態(tài)庫)
2.CocoaPod
3.Carthage(僅支持iOS8以上)
4.Static Framework
Realm 中的相關(guān)術(shù)語
為了能更好的理解Realm的使用,先介紹一下涉及到的相關(guān)術(shù)語。
RLMRealm:Realm是框架的核心所在,是我們構(gòu)建數(shù)據(jù)庫的訪問點(diǎn),就如同Core Data的管理對(duì)象上下文(managed object context)一樣。出于簡單起見,realm提供了一個(gè)默認(rèn)的defaultRealm( )的便利構(gòu)造器方法。
RLMObject:這是我們自定義的Realm數(shù)據(jù)模型。創(chuàng)建數(shù)據(jù)模型的行為對(duì)應(yīng)的就是數(shù)據(jù)庫的結(jié)構(gòu)。要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)模型,我們只需要繼承RLMObject,然后設(shè)計(jì)我們想要存儲(chǔ)的屬性即可。
關(guān)系(Relationships):通過簡單地在數(shù)據(jù)模型中聲明一個(gè)RLMObject類型的屬性,我們就可以創(chuàng)建一個(gè)“一對(duì)多”的對(duì)象關(guān)系。同樣地,我們還可以創(chuàng)建“多對(duì)一”和“多對(duì)多”的關(guān)系。
寫操作事務(wù)(Write Transactions):數(shù)據(jù)庫中的所有操作,比如創(chuàng)建、編輯,或者刪除對(duì)象,都必須在事務(wù)中完成?!笆聞?wù)”是指位于write閉包內(nèi)的代碼段。
查詢(Queries):要在數(shù)據(jù)庫中檢索信息,我們需要用到“檢索”操作。檢索最簡單的形式是對(duì)Realm( )數(shù)據(jù)庫發(fā)送查詢消息。如果需要檢索更復(fù)雜的數(shù)據(jù),那么還可以使用斷言(predicates)、復(fù)合查詢以及結(jié)果排序等等操作。
RLMResults:這個(gè)類是執(zhí)行任何查詢請(qǐng)求后所返回的類,其中包含了一系列的RLMObject對(duì)象。RLMResults和NSArray類似,我們可以用下標(biāo)語法來對(duì)其進(jìn)行訪問,并且還可以決定它們之間的關(guān)系。不僅如此,它還擁有許多更強(qiáng)大的功能,包括排序、查找等等操作。
Realm 的基本使用
1.創(chuàng)建數(shù)據(jù)庫
- 1.使用系統(tǒng)默認(rèn)的數(shù)據(jù)庫
- 2.自定義數(shù)據(jù)庫(代碼實(shí)現(xiàn)的是自定義的數(shù)據(jù)庫)
Object-C
RLMRealm *realm = [RLMRealm defaultRealm];
- (BOOL) initRealm {
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
URLByAppendingPathComponent:[NSString stringWithFormat:@"Message%lu",(unsigned long)self.messageType]]
URLByAppendingPathExtension:@"realm"];
debugLog(@"Realm file path: %@", config.fileURL);
NSError *error;
_realm = [RLMRealm realmWithConfiguration:config error:&error];
if (nil != error) {
NSLog(@"Create Realm Error");
return NO;
}
debugLog(@"create realm success");
return YES;
}
Swift
// MARK: - 初始化Rleam數(shù)據(jù)庫
func createDataBase() {
var config = Realm.Configuration()
config.fileURL = config.fileURL?.deletingLastPathComponent().appendingPathComponent("HJQ").appendingPathExtension("Realm")
var realm: Realm?
do {
realm = try Realm.init(configuration: config)
} catch {
debugLog(error)
}
debugLog(realm)
}
2.建表
Object-C
#import <Foundation/Foundation.h>
#import <Realm/Realm.h>
@interface UserMessageList : RLMObject
@property NSString *title;
@property NSInteger linkType;
@property NSInteger messageId;
@property BOOL readStatus;
@property NSString *content;
@property long long createTime;
@property NSString *picUrl;
@end
RLM_ARRAY_TYPE(UserMessageList);
@interface GFBUserMessageMD : RLMObject
@property NSString *username;
@property RLMArray<UserMessageList> *userMessageList;
@end
RLM_ARRAY_TYPE(GFBUserMessageMD);
注意,RLMObject 官方建議不要加上 Objective-C的property attributes(如nonatomic, atomic, strong, copy, weak 等等)假如設(shè)置了,這些attributes會(huì)一直生效直到RLMObject被寫入realm數(shù)據(jù)庫。
RLM_ARRAY_TYPE宏創(chuàng)建了一個(gè)協(xié)議,從而允許 RLMArray<GFBUserMessageMD>語法的使用。如果該宏沒有放置在模型接口的底部的話,您或許需要提前聲明該模型類。
關(guān)于RLMObject的的關(guān)系
1.對(duì)一(To-One)關(guān)系
對(duì)于多對(duì)一(many-to-one)或者一對(duì)一(one-to-one)關(guān)系來說,只需要聲明一個(gè)RLMObject子類類型的屬性即可
2.對(duì)多(To-Many)關(guān)系(重點(diǎn)介紹)
通過 RLMArray類型的屬性您可以定義一個(gè)對(duì)多關(guān)系。如上面代碼例子,@property RLMArray<UserMessageList> *userMessageList;
3.反向關(guān)系(Inverse Relationship)
#import "GFBUserMessageMD.h"
@implementation GFBUserMessageMD
// 為了保證表的唯一性,設(shè)置主鍵
+ (NSString *)primaryKey {
return @"username";
}
@end
@implementation UserMessageList
@end
Swift
import UIKit
import RealmSwift
>
class UserMessageMD: Object {
dynamic var title: String?
dynamic var messageID: String!
}
>
class UserMD: Object {
dynamic var userID: String!
dynamic var name: String!
>
// List 是個(gè)泛型
var userMessages = List<UserMessageMD>()
>
// MARK: - 設(shè)置主鍵
override static func primaryKey() -> String? {
return "userID"
}
}
3.數(shù)據(jù)的插入
Object-C
- (void) insertOrCreateData:(GFBSysMessageListMD *)md {
GFBUserMessageMD *userMD;
NSString *userId = [QFUserInfo shareInfo].username;
if (! userId) {
return;
}
// 查詢當(dāng)前是否有這個(gè)表
if ([GFBUserMessageMD objectInRealm:_realm forPrimaryKey:userId]) {
userMD = [GFBUserMessageMD objectInRealm:_realm forPrimaryKey:userId];
}else {
userMD = [GFBUserMessageMD new];
userMD.username = userId;
}
[_realm transactionWithBlock:^{
for (int i = 0; i < md.data.content.count; i++) {
SysMessageListContent *model = md.data.content[i];
UserMessageList *userMessageMD;
NSPredicate *pred = [NSPredicate predicateWithFormat:@"messageId = %li",model.id];
if([userMD.userMessageList indexOfObjectWithPredicate:pred] != NSNotFound) { // 如果該數(shù)據(jù)已經(jīng)存在則無需重新插入
NSUInteger index = [userMD.userMessageList indexOfObjectWithPredicate:pred];
userMessageMD = userMD.userMessageList[index];
debugLog(@"我是查詢的結(jié)果%lu",index);
}else{
// 插入新的數(shù)據(jù)
userMessageMD = [UserMessageList new];
userMessageMD.title = model.title;
userMessageMD.messageId = model.id;
userMessageMD.content = model.content;
userMessageMD.createTime = model.createTime;
userMessageMD.readStatus = NO;
userMessageMD.linkType = model.linkType;
userMessageMD.picUrl = model.picUrl;
[userMD.userMessageList addObject:userMessageMD];
}
}
[GFBUserMessageMD createOrUpdateInRealm:_realm withValue:userMD];
}];
}
Swift
// MARK: - 建表插入數(shù)據(jù)
func insertData() {
let realm = try! Realm()
// 通過主鍵查找到對(duì)應(yīng)的數(shù)據(jù)
var userMD = realm.object(ofType: UserMD.self,forPrimaryKey: "")
if userMD == nil {
userMD = UserMD()
}
let messageID = "10000"
try! realm.write {
var userMessageMD: UserMessageMD? = nil
let pred = NSPredicate(format: "messageID = \(messageID) ")
let resultIndex = userMD!.userMessages.index(matching: pred)
if resultIndex != NSNotFound { // 已經(jīng)存在
userMessageMD = userMD!.userMessages[resultIndex!]
}else {
userMessageMD = UserMessageMD()
// 不存在則繼續(xù)追加
userMD?.userMessages.append(userMessageMD!)
}
realm.add(userMD!, update: true)
}
}
4.數(shù)據(jù)的更新
- (void) updateReadStatus:(NSInteger) index {
GFBUserMessageMD *userMD;
NSString *userId = [QFUserInfo shareInfo].username;
if (! userId) {
return;
}
// 查詢當(dāng)前是否有這個(gè)表
if ([GFBUserMessageMD objectInRealm:_realm forPrimaryKey:userId]) {
userMD = [GFBUserMessageMD objectInRealm:_realm forPrimaryKey:userId];
}
if (userMD) {
[_realm transactionWithBlock:^{
UserMessageList *userMessageMD = userMD.userMessageList[index];
userMessageMD.readStatus = YES;
[GFBUserMessageMD createOrUpdateInRealm:_realm withValue:userMD];
}];
}
}
5.數(shù)據(jù)的查詢
Object-C
// 查找所有的數(shù)據(jù)
- (void) queryAllData {
GFBUserMessageMD *userMD;
NSString *userId = [QFUserInfo shareInfo].username;
if (! userId) {
return;
}
// 查詢當(dāng)前是否有這個(gè)表
if ([GFBUserMessageMD objectInRealm:_realm forPrimaryKey:userId]) {
userMD = [GFBUserMessageMD objectInRealm:_realm forPrimaryKey:userId];
}
if (userMD) {
debugLog(@"當(dāng)前存在的數(shù)據(jù)%@",userMD.userMessageList);
}
}
Swift
// MARK: - 查詢數(shù)據(jù)
func queriesDatas() {
let realm = try! Realm()
// 通過主鍵查找到對(duì)應(yīng)的數(shù)據(jù)
let userMD = realm.object(ofType: UserMD.self,forPrimaryKey: "")
debugLog(userMD)
}