XMPP介紹二:Core Data

本人錄製技術視頻地址:https://edu.csdn.net/lecturer/1899 歡迎觀看。

上一節,介紹了實現XMPP所必須的服務器及數據庫搭建工作,如果感興趣的話,請查看上一篇博客:XMPP介紹一:服務器及數據庫搭建。後端的準備工作介紹完畢,現在來介紹一下,客戶端開發XMPP所必須具備的儲備知識。XMPP架構在客戶端的數據持久化操作是通過Core Data實現的,所以這一節的內容,我將爲大家詳細的說說Core Data的理論知識及基本使用。

一、Core Data基本介紹

Core Data 是蘋果實現的一種數據持久化機制,可以方便的用來對SQLite進行CRUD操作。我們經常使用的第三方庫FMDB也是對SQLite進行的數據庫操作。Core Data的特點就是不用自己書寫SQL語句,只需要手動配置對象模型,Core Data這個框架就會自動的幫助我們完成對象實體的映射工作,說簡單一點,就是它會自動的幫助我們生成操作數據庫的SQL語句。它可以將OC對象整體映射到數據庫表中,也可以將數據庫表中的數據映射成OC對象直接取出使用。Core Data的核心思想:ORM。 ORM框架的思想最早起源於Java的Hibernate持久化框架;後來.NET 也出了一套同樣的機制,稱之爲Entity Framework;到了iOS5.0中,就稱之爲Core Data,其實都是同一個意思。爲了說明ORM的中心思想,我繪製了一張分析圖,如下:


1. 對象模型: 現在大部分的編程語言都支持面向對象思想,上圖中左側圖形就表示實體。

2. 關係模型: 指的就是關係型數據庫存儲的結構,上圖中右側表結構。

3. Core Data的工作就是負責這兩種模型之間的數據切換,方便的完成數據存取(避免了書寫那些SQL語句)。


二、Xcode創建模型文件的過程

1. 選擇模板



2. 添加實體(點擊Add Entity)


3. 添加屬性



4. 創建NSManagedObject的子類

默認情況下,利用Core Data取出的實體都是NSManagedObject類型的,能夠利用鍵-值對來存取數據。但是一般情況下,實體在存取數據的基礎上,有時還需要添加一些業務方法來完成一些其他任務,那麼就必須創建NSManagedObject的子類

4.1 創建子類

4.2 選擇模型文件 

 

4.3 選擇需要創建子類的實體 


4.4 創建完畢後,就可以看到類的信息(包括分類)


4.5 類中的內容

Employee.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

NS_ASSUME_NONNULL_BEGIN

@interface Employee : NSManagedObject

// Insert code here to declare functionality of your managed object subclass

@end

NS_ASSUME_NONNULL_END

#import "Employee+CoreDataProperties.h"

Employee+CoreDataProperties.h

#import "Employee.h"

NS_ASSUME_NONNULL_BEGIN

@interface Employee (CoreDataProperties)

@property (nullable, nonatomic, retain) NSString *name;
@property (nullable, nonatomic, retain) NSNumber *age;
@property (nullable, nonatomic, retain) NSNumber *height;

@end

NS_ASSUME_NONNULL_END

三、Core Data中的核心對象

在完成上述操作後,模型文件就已經處理完畢了,接下來的工作就是創建SQLite數據庫,及建立相應的連接關係,完成CRUD等操作,在介紹具體的操作之前,我們先來分析一下Core Data中的核心對象,如下圖:



1. NSManagedObjectContext:Core Data的請求上下文,就是利用它來完成CRUD等操作。

2. NSPersistentStoreCoordinator:持久化存儲調度器,可以利用它來指定並且創建用來持久化數據的SQLite數據庫。

3. NSManagedObjectModel:Core Data操作數據的模型對象,可以將所有的模型文件關聯起來(模型文件就是後綴名爲xcdatamodeld的裏面所有的實體文件)。

4. NSEntityDescription:描述實體中屬性的詳細信息。

有了以上的分析,我們就可以用代碼來實現上面一系列的流程工作。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupContext];
}

- (void)setupContext {
    // 創建上下文對象
    self.context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    
    // 創建一個模型對象,nil參數表示會將所有模型文件 關聯起來
    NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
    
    // 持久化存儲調度器
    NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    
    // 存儲數據庫路徑
    NSString *document = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *sqlitePath = [document stringByAppendingPathComponent:@"company.sqlite"];
    NSError *error;
    [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlitePath] options:nil error:&error];
    self.context.persistentStoreCoordinator = store;<span style="color:#00afca;">
}</span>

四、利用Core Data完成CRUD操作

1. Create(增加操作)

- (IBAction)addRecord:(id)sender {
    
    Employee *employee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:self.context];
    employee.name = @"kimmi";
    employee.age = @23;
    employee.height = @1.60;
    
    NSError *error;
    [self.context save:&error];
    if (error) {
        NSLog(@"保存失敗");
    } else {
        NSLog(@"保存成功");
    }
}

注:Core Data的增、刪、改操作,最終都需要執行 NSManagedObjectContext實例對象的save方法。在調用save方法之前執行的操作全是在內存中執行的,而只有save操作纔會將數據的變更同步到SQLite中!

2. Delete (刪除操作)

- (IBAction)deleteRecord:(id)sender {
    // 1. 先獲取指定對象
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"jason"];
    request.predicate = pre;
    NSArray *items = [self.context executeFetchRequest:request error:nil];
    
    // 2. 先刪除內存中的數據
    for (Employee *emp in items) {
        [self.context deleteObject:emp];
    }
    
    // 3. 同步到數據庫
    [self.context save:nil];
}

3. Update (更新操作)

- (IBAction)updateRecord:(id)sender {
    // 1. 先獲取指定對象
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"rose"];
    request.predicate = pre;
    NSArray *items = [self.context executeFetchRequest:request error:nil];
    
    // 2. 修改內存中的數據
    if (items.count > 0) {
        Employee *emp = [items firstObject];
        emp.age = @(24);
        emp.height = @(1.83);
    }
    
    // 3. 同步到數據庫
    [self.context save:nil];
}

4. Retrieve (查詢操作)

- (IBAction)searchRecords:(id)sender {
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    
    // 1. 過濾條件(精確查找)
//    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"jason"];
//    request.predicate = pre;
    
    // 2. 過濾條件(大小查找)
//    NSPredicate *pre = [NSPredicate predicateWithFormat:@"height > %@ and age > %@", @(1.0), @(20)];
//    request.predicate = pre;
    
    // 3. 分頁查詢
//    request.fetchOffset = 4;
//    request.fetchLimit = 2;
    
    NSError *error;
    // 查詢結果
    NSArray *items = [self.context executeFetchRequest:request error:&error];
    
    if (error) {
        NSLog(@"查詢失敗");
    } else {
        for (Employee *employee in items) {
            NSLog(@"name:%@, age:%@, height:%@", employee.name, employee.age, employee.height);
        }
    }
}

- (IBAction)fuzzySearch:(id)sender {
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    
    // 1. 匹配開頭
//    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name beginswith %@", @"ro"];
//    request.predicate = pre;
    
    // 2. 匹配結尾
//    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name endswith %@", @"mi"];
//    request.predicate = pre;
    
    // 3. contains
//    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name contains %@", @"m"];
//    request.predicate = pre;
    
    // 4. like
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name like %@", @"*m*"];
    request.predicate = pre;
    
    NSError *error;
    // 查詢結果
    NSArray *items = [self.context executeFetchRequest:request error:&error];
    
    if (error) {
        NSLog(@"查詢失敗");
    } else {
        for (Employee *employee in items) {
            NSLog(@"name:%@, age:%@, height:%@", employee.name, employee.age, employee.height);
        }
    }
}

5. 處理多表關聯的問題

5.1 我們在原來的模型上面,再建立一個Department實體


5.2 在Employee表中建立關聯,即每一個員工隸屬於某一個部門(depart這個Relationship)


5.3 建立完成這個關聯關係後,我們再來看看Employee+CoreDataProperties.h這個文件,可以明顯的看出,

多了一個depart對象屬性。

#import "Employee.h"

NS_ASSUME_NONNULL_BEGIN

@interface Employee (CoreDataProperties)

@property (nullable, nonatomic, retain) NSString *name;
@property (nullable, nonatomic, retain) NSNumber *age;
@property (nullable, nonatomic, retain) NSNumber *height;
@property (nullable, nonatomic, retain) NSString *city;
@property (nullable, nonatomic, retain) Department *depart;

@end

NS_ASSUME_NONNULL_END


5.4 利用Core Data完成多表關聯的操作

- (IBAction)queryRecords:(id)sender {
    // 查詢ios部門的員工
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"depart.name = %@", @"ios"];
    request.predicate = pre;
    
    NSError *error;
    // 查詢結果
    NSArray *items = [self.context executeFetchRequest:request error:&error];
    
    if (error) {
        NSLog(@"查詢失敗");
    } else {
        for (Employee *employee in items) {
            NSLog(@"name:%@, age:%@, height:%@", employee.name, employee.age, employee.height);
        }
    }
}
注意到,NSPredicate中的過濾條件是 depart.name, 它就是直接操作了Employee對象中的depart對象的name屬性。

這樣,就不用自己書寫數據庫關聯表的SQL語句了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章