[iOS] 在iOS裡操作SQLite筆記- CoreData Framework(ORM) (二)

  • 10012
  • 0
  • iOS
  • 2015-03-01

[iOS] 在iOS裡操作SQLite筆記- CoreData Framework(ORM) (二)

 

上一篇再取出SQLite的資料的時候,我們用底下這個方法

- (NSArray *) statementtoMSarray:(sqlite3_stmt *) sqlstmt{…}

來將資料回傳成一個陣列。這有點像早期寫ASP的時候,當我們送出一個Conn.execute物件會回傳一個

Recordset物件,然而要存取這個物件,利用上一篇的做法就比較像以往在存取一個RS物件,在這樣的存取資料方式,就會屬於弱型別的資料存取方式。

這是一個動態陣列物件,會多做這個將statement轉換成陣列物件的動作,是因為範例中我們要把資料塞去給UITableView。

- (NSArray *) statementtoMSarray:(sqlite3_stmt *) sqlstmt{

TabelDataes = [[NSMutableArray alloc]init];

NSString *data;

//這邊使用While迴圈來跑資料,把statement裡的資料一筆筆存進陣列裡面

while (sqlite3_step(sqlstmt) == SQLITE_ROW) {

data = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlstmt,0)];

[TabelDatas addObject:data];

}

return TabelDatas;

}

看完上述資料,可以發現這種弱型別的存取方式,如果當Statement不單只是一維陣列的時候,你就要利用sqlite3_column_text(sqlstmt,0) , sqlite3_column_text(sqlstmt,1) , sqlite3_column_text(sqlstmt,2) 這樣的語法來取出相對應的資料。當資料維度一變大,在維護上就不比較不容易閱讀。接著要討論的是在iOS裡存取SQLite的ORM Framework架構Core data。

 

Core Data 主要分成下列個部分與運作流程

Managed Object Context : 這是CoreData 所有部分中對我們最重要的,這個物件是當我們要對Sqlite去新增,刪除,修改,查詢這些動作時,我們需要呼叫此類別裡面的方法。Managed Object Context 這個類別記載了這個App在記憶體中的所有Entity。當我們向Core Data 要求載入物件(Managed Object,一筆資料時),會先向Managed Object Context提出要求。假如記憶體中沒有我們要求的這筆資料,Managed Object Context 擇向Persistent Store Coordinator發出請求。Persistent Store Coordinator會追蹤管理 Persistent Object Store 物件對資料庫的讀寫。

 

Persistent Store Coordinator: 可以想像成資料庫的連線物件,我們會在這裡去設定SQLite資料庫的實際位置跟名稱。告知應用App資料要被我們儲存到哪一個資料庫去。當Managed Object Context要對資料庫做任何的操作動作,CoreData底層都會呼叫這個物件。

 

Managed Object Model: 這部分可以想像它是database schema,它是一個儲存在資料庫裡面的資料描述類別(或稱之為Entities)。當App對Managed Object Context提出資料存取要求,Managed Object Context 擇向Persistent Store Coordinator發出請求。Persistent Store Coordinator會追蹤管理 Persistent Object Store 物件對資料庫的讀寫。而Persistent Object Store 物件取得資料庫的資料後,Persistent Store Coordinator物件會呼叫Managed Object Model 來依據 Data Model 的 Entity 類別生成 Managed Object物件並且載入記憶體當中,此時App就可以透過Managed Object Context物件所提供的方法對記憶體中的物件做操作,這個操作是離線的,所以如果要對資料庫更新這筆資料,就需要呼叫儲存的動作。

流程了解後來新建一個iOS CoreData專案做測試

 

1. 新增Xcode專案,專案名稱命名為[CoreData]。

2. 加入CoreData.Framework參考

clip_image002

 

3. 新增DataModel。

clip_image004

 

這邊命名為country,副檔名為xcdatamodele。

clip_image006

 

4. 新增Entity

clip_image008

 

5. 新增NSManagedObjectsubclass 類別

clip_image009

 

接著選擇我們剛剛建立的Entity。

clip_image011

 

按下確定後,會看見相對應的類別檔案備建立起來。這個檔案稍後會被拿來作為轉型(Boxing),讓上層的App可以以物件的方式來操作記憶體裡面的資料。

clip_image013

 

6.在AppDelegate.h檔案裡面做底下的宣告

#import 

@interface AppDelegate : UIResponder {

//增加 Core Data 的成員變數

NSManagedObjectContext *m_managedObjectContext;

NSPersistentStoreCoordinator *m_persistentCoordinator;

NSManagedObjectModel *m_managedObjectModel;

}

@property (strong, nonatomic) UIWindow *window;

//增加Core Data的成員變數property定義

@property (nonatomic, retain) NSManagedObjectContext *m_managedObjectContext;

@property (nonatomic, retain) NSManagedObjectModel *m_managedObjectModel;

@property (nonatomic, retain) NSPersistentStoreCoordinator *m_persistentCoordinator;

//這是用來取得資料庫實體位置,Sqlite實體位置會被放置在這個路徑之下 ~/library/Application Support/iPhone Simulator/版本/Applications/你的App/Documents/

-(NSURL *)applicationDocumentsDirectory;

//傳回這個APP的物件本文管理,用來作物件同步

-(NSManagedObjectContext*)managedObjectContext;

//傳回這個APP中管理資料庫的persistent store coordinator 物件

//Persistent Store Coordinator相當於資料文件管理器,處理底層的對資料文件的讀取與寫入。一般我們無需與它打交道。

-(NSPersistentStoreCoordinator *)persistentStoreCoordinator;

//傳回這個APP中

//Managed Object Model是描述應用程序的資料模型,這個模型包含實體(Entity),特性(Property),讀取請求(Fetch Request )等。

-(NSManagedObjectModel*)managedObjectModel;

@end

 

7. 在AppDelegate.m檔案裡面做底下的宣告

7.1 synthesize 這三個變數

@synthesize m_managedObjectContext;

@synthesize m_persistentCoordinator;

@synthesize m_managedObjectModel;

 

7.1 實作applicationDocumentsDirectory方法與其內容

這個方法是要取得Sqlite資料庫的實體路徑位置。

-(NSURL *)applicationDocumentsDirectory{

return [[[NSFileManager defaultManager]URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]lastObject];

}

 

 

7.3 實作managedObjectContext方法

這個方法會回傳一個物件NSManagedObjectContext實例,App可以藉由其提供的方法來對資料庫做操作的動作。

-(NSManagedObjectContext *)managedObjectContext{

if(m_managedObjectContext != nil){

return m_managedObjectContext;

}

//在managedObjectContext裡面會對persistentStoreCoordinator發出資料庫操作的請求,所以在此可以看到這邊去建立了一個NSPersistentStoreCoordinator 實例

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

if(coordinator != nil){

m_managedObjectContext = [[NSManagedObjectContext alloc] init];

[m_managedObjectContext setPersistentStoreCoordinator:coordinator];

}

return m_managedObjectContext;

}

 

7.4 實作persistentStoreCoordinator方法

這個方法會回傳一個物件NSPersistentStoreCoordinator實例。

-(NSPersistentStoreCoordinator *)persistentStoreCoordinator{

if (m_persistentCoordinator != nil){

return m_persistentCoordinator;

}

//指定實際存放資料庫的位置,這邊會呼叫applicationDocumentsDirectory方法取得資料庫的實體路徑

NSURL *storeURL = [[self applicationDocumentsDirectory]URLByAppendingPathComponent:@"data.sqlite"];

NSError *error = nil;

//呼叫managedObjectModel,經由managedObjectModel讀取資料模型來生成被管理的物件Managed object

m_persistentCoordinator = [[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:[self managedObjectModel]];

if(![m_persistentCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){

NSLog(@"讀取資料時發生錯誤 %@,%@",error,[error userInfo]);

abort();

}

return m_persistentCoordinator;

}

 

 

7.4實作managedObjectModel方法

-(NSManagedObjectModel *)managedObjectModel{

if(m_managedObjectModel != nil){

return m_managedObjectModel;

}

//讀取資料模型來生成被管理的物件Managedobject

NSURL *modelURL = [[NSBundle mainBundle]URLForResource:@"country" withExtension:@"momd"];

m_managedObjectModel = [[NSManagedObjectModel alloc]initWithContentsOfURL:modelURL];

return m_managedObjectModel;

}

 

 

8.在iPhoneStory Board上建立底如下圖的控制項。

在ViewController.h 檔案裡面的程式如下

#import 

@class AppDelegate;
@interface ViewController : UIViewController{
    AppDelegate* appDelegate;
    NSArray *data;
}

@property (weak, nonatomic) IBOutlet UITextField *mylabel1;
@property (weak, nonatomic) IBOutlet UITextField *mylabel2;
@property (weak, nonatomic) IBOutlet UITableView *mytable;

- (IBAction)mybtn:(id)sender;

- (IBAction)get_btn:(id)sender;

@end

 

9.在在ViewController.m檔案裡面新增下方程式

9.1在資料新增事件中,首先呼叫了NSEntityDescription物件的insertNewObjectForEntityForName方法

,這個方法會在記憶體裡面新增一筆資料,接著我們用先前建立的NSManagedObject Country轉型,讓App可以以物件的方式來操作這筆資料。最後在藉由m_managedObjectContext的save方法將資料同步進SQLite。

//寫資料到Sqlite的事件

- (IBAction)mybtn:(id)sender {

NSLog(@"準備寫資料進入資料庫");

Country *country =(Country*)[NSEntityDescription insertNewObjectForEntityForName:@"Country" inManagedObjectContext:[appDelegate managedObjectContext]];

country.pk = self.mylabel1.text;

country.name = self.mylabel2.text;

NSError* error = nil;

if(![appDelegate.m_managedObjectContext save:&error]){

NSLog(@"新增資料遇到錯誤");

}

}

 

 

9.2 相對的再取出資料的部分,比較簡單的方式就是用NSFetchRequest物件取得SQLite資料庫的實體集合,

並且這個物件可以直接被回傳為陣列物件,這樣就不用像上一篇研究一樣,我們還要去自己動手腳把Statement轉成陣列物件,在這個步驟倒是省了不少的工。

//讀取資料的事件

- (IBAction)get_btn:(id)sender {

NSFetchRequest *fetch = [[NSFetchRequest alloc]init];

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Country" inManagedObjectContext:appDelegate.managedObjectContext];

[fetch setEntity:entity];

data = [appDelegate.managedObjectContext executeFetchRequest:fetch error:nil];

[self.mytable reloadData];

NSLog(@"讀取資料庫完畢");

}

 

 

10.執行程式可以對資料庫做操作了。

clip_image015

 

11.事情總是有一好沒兩好。

當我們用軟體去開啓由CoreData幫我們建立的資料庫的時候,會看到,所有的資料庫以及資料欄位名稱都被加了一個大寫的Z當做開頭字母,並且還幫我們多建了兩個資料庫,分別為Z_METDATA還有Z_PRIMARYKEY。

主要的資料欄位裡面也多了三個系統幫我們建立的欄位,分別是Z_PK,Z_ENT,Z_OPT。

我嘗試找了很多文章,希望在CoreData中引入沒有加入這些系統資料的SQLite資料庫,然後來結合CoreData的ORM架構對資料庫做操作,不過未果。所以當你的資料庫是預先就填充了一些資料的時候,就必須使用CoreData幫你建出來的資料庫當基底了,人參啊……………只好繼續動些手腳了

clip_image017

 

範例程式下載:https://github.com/twbenlu/iOSCoredata

參考資訊:

Core Data FAQ

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdFAQ.html

Core Data Tutorial for iOS: Getting Started

http://www.raywenderlich.com/934/core-data-tutorial-for-ios-getting-started

Using a Pre-Populated SQLite Database with Core Data on iPhone OS 3.0

http://ablogontech.wordpress.com/2009/07/13/using-a-pre-populated-sqlite-database-with-core-data-on-iphone-os-3-0/

Any way to pre populate core data?

http://stackoverflow.com/questions/2230354/any-way-to-pre-populate-core-data

[iOS] Core Data 基礎概念

http://cg2010studio.wordpress.com/2012/12/22/ios-core-data-基礎概念/