一.文章概要
當(dāng)前使用工具是XCode7.
這篇文章主要是寫了對于基本數(shù)據(jù)類型的"增刪改查"的操作,至于特殊類型比如UIImage,NSArray,NDDictionary我們在之后的幾篇文章中講到.
二.前話
從平常的使用來說,FMDB和coreData是項(xiàng)目中最經(jīng)常使用的兩個(gè)數(shù)據(jù)庫持久化方式.
CoreData 首先它是一個(gè) ORM框架姜胖。 SQLite 與 FMDB 其實(shí)都脫離不了關(guān)系型(relational)數(shù)據(jù) 而我們平時(shí)使用的編程語言卻是面向?qū)ο蟮奶陈樱际菍ο笮偷摹?br>
在 iOS 中末融,coreData 和 FMDB 關(guān)于這兩者的區(qū)別,我認(rèn)為主要是如下幾個(gè)方面
coreData
優(yōu)點(diǎn)
(1)映射之后 直接操作對象就能進(jìn)行增刪改查 更貼近 程序員的生活稳衬。
(2)我們不需要再寫SQL語句豌汇,避免了 SQL語句的繁瑣衡楞,比如說 SQL語句經(jīng)常要寫一大堆的包含 values(name,sex) 還要有對應(yīng)個(gè)數(shù)的?與前邊的()中的屬性個(gè)數(shù)對應(yīng)
缺點(diǎn)
(1) coreData沒有 java 中的 ORM 框架快速.尤其是多表操作的時(shí)候,效率并不是很高. java中的框架(Hibernate:特點(diǎn):可以把 SQL語句和面型對象的查詢語句混合使用.舉個(gè)例子:它可以把復(fù)雜的多表操作語句用一行查詢語句來執(zhí)行完畢)。而 coreData操作多表的時(shí)候,需要?jiǎng)?chuàng)建多個(gè)對應(yīng)數(shù)量的Requset,description,謂詞,篩選器恩溅。
(2)就單純的批量插入數(shù)據(jù)的速度 相比較FMDB 稍微慢一點(diǎn)點(diǎn)
三.FMDB
如果你之前用過sqlSever數(shù)據(jù)庫的話,繼續(xù)往下看,當(dāng)然你也可以直接跳過這一小節(jié)
首先,想必軟件工程專業(yè)或者相關(guān)專業(yè)的同學(xué)在學(xué)校學(xué)的就是sqlSever,對應(yīng)的數(shù)據(jù)庫課程也是免不了的.那么sql語句是我們必須要學(xué)習(xí)的."簡單查詢""多表查詢"等等這些都是我們要學(xué)習(xí)的.恭喜你,你已經(jīng)有了一定學(xué)習(xí)FMDB的基礎(chǔ)了.
再進(jìn)一步說,你之前從事java或者安卓開發(fā)工作的時(shí)候,用到了sqlSever,那么再次恭喜你,你基本已經(jīng)可以FMDB的那些基本功能了.
由此,我們可見,FMDB的使用方式和之前在其他語言中使用sqlSever數(shù)據(jù)庫的時(shí)候,是非常相似的.之前我們在使用sqlSever提到的那些關(guān)鍵點(diǎn)在FMDB中,也有體現(xiàn),比如"打開數(shù)據(jù)庫,關(guān)閉數(shù)據(jù)庫""手敲sql語句,執(zhí)行sql語句"......所以,FMDB,對于之前上手還是很熟悉,很簡單的.
你到現(xiàn)在還沒有使用過FMDB. 那也不用太煩惱和害怕,數(shù)據(jù)庫的基本格式都是固定的.既然是固定的,拷貝一段改改就可以了.
擴(kuò)展
(1)問:"什么時(shí)候使用coredata 什么時(shí)候使用FMDatabases?"
答:
a. CoreData 在公司使用的比較少隔箍,用戶的比較多的是FMDatabases。
b.數(shù)據(jù)存儲的結(jié)構(gòu)比較簡單的時(shí)候脚乡,使用CoreData
c.CoreData 開發(fā)效率會高點(diǎn)蜒滩,為什么?因?yàn)樗敲嫦驅(qū)ο蟮模也挥脤憇ql語句.
FMDatabases 數(shù)據(jù)結(jié)果比較復(fù)雜的時(shí)候奶稠,表與表之間的關(guān)聯(lián)比較多的時(shí)候使用.
(2)coreData 其實(shí)底層也是要寫sql 語句的俯艰,coreData 幫我們把sql語句封裝。
四.你要的干貨,利用coreData來持久化數(shù)據(jù)
第一步,創(chuàng)建'模型文件'
第二步,創(chuàng)建'實(shí)體Entity'
創(chuàng)建實(shí)體對象
創(chuàng)建實(shí)體對象的屬性(屬性名,屬性類型和關(guān)聯(lián)實(shí)體)
問:什么是關(guān)聯(lián)實(shí)體???
答:其實(shí)就是我們創(chuàng)建另外的一個(gè)實(shí)體.這里添加關(guān)聯(lián)實(shí)體的意思就是,舉個(gè)例子我們?nèi)祟愑袀€(gè)寵物狗,那么反過來寵物狗也有個(gè)主人是人類. 這里說白了就是兩個(gè)對象之間的相互關(guān)聯(lián)
溫馨提示
第三步,創(chuàng)建'創(chuàng)建NSManagedObject的子類文件'
以下的代碼是系統(tǒng)默認(rèn)生成的,這里貼出是僅供對比查驗(yàn)使用,在文篇文章中沒有其它的意義.
創(chuàng)建出的子類代碼-Book+CoreDataProperties.h:
#import "Book.h"
NS_ASSUME_NONNULL_BEGIN
@interface Book (CoreDataProperties)
@property (nullable, nonatomic, retain) NSNumber *bookID;
@property (nullable, nonatomic, retain) NSString *bookName;
@property (nullable, nonatomic, retain) Student *student;
@end
NS_ASSUME_NONNULL_END
創(chuàng)建出的子類代碼-Book+CoreDataProperties.m:
#import "Book+CoreDataProperties.h"
@implementation Book (CoreDataProperties)
@dynamic bookID;
@dynamic bookName;
@dynamic student;
@end
創(chuàng)建出的子類代碼-Book.h:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class Student;
NS_ASSUME_NONNULL_BEGIN
@interface Book : NSManagedObject
// Insert code here to declare functionality of your managed object subclass
@end
NS_ASSUME_NONNULL_END
#import "Book+CoreDataProperties.h"
創(chuàng)建出的子類代碼-Book.m:
#import "Book.h"
#import "Student.h"
@implementation Book
// Insert code here to add functionality to your managed object subclass
@end
創(chuàng)建出的子類代碼-Student+CoreDataProperties.h:
#import "Student.h"
NS_ASSUME_NONNULL_BEGIN
@interface Student (CoreDataProperties)
@property (nullable, nonatomic, retain) NSNumber *id;
@property (nullable, nonatomic, retain) NSString *name;
@property (nullable, nonatomic, retain) Book *book;
@end
NS_ASSUME_NONNULL_END
創(chuàng)建出的子類代碼-Student+CoreDataProperties.m:
#import "Student+CoreDataProperties.h"
@implementation Student (CoreDataProperties)
@dynamic id;
@dynamic name;
@dynamic book;
@end
創(chuàng)建出的子類代碼-Student.h:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class Book;
NS_ASSUME_NONNULL_BEGIN
@interface Student : NSManagedObject
// Insert code here to declare functionality of your managed object subclass
@end
NS_ASSUME_NONNULL_END
#import "Student+CoreDataProperties.h"
創(chuàng)建出的子類代碼-Student.m:
#import "Student.h"
#import "Book.h"
@implementation Student
// Insert code here to add functionality to your managed object subclass
@end
第四步,創(chuàng)建'coreData的管理類'
我們暫時(shí)不做封裝,直接在UIViewController演示.
第五步,coreData操作-創(chuàng)建(獲取)上下文
這里的上下文在OC中,類似我們coreAnimation中用到的圖形上下文. OC中哪些有上下文?
在正式看下邊的'增刪改查'代碼之前,我們需要也別注意的是:
(0)在我們每次執(zhí)行四個(gè)操作的時(shí)候,都首先要保證上下文存在(還活著)
(1)并且代碼執(zhí)行在'異步主隊(duì)列中'
(2)'增'部分:創(chuàng)建保存對象部分,不是直接new或者alloc-init
(3)'查'部分:NSPredicate過濾條件(精確和非精確)
(4)'改'部分:'查'部分:NSPredicate過濾條件(精確和非精確)
(5)'刪'部分:'查'部分:NSPredicate過濾條件(精確和非精確)
第六步,coreData操作-創(chuàng)建上下文
上邊提到我們的演示文件類型是UIViewController,所以是在'viewDidLoad'方法中創(chuàng)建上下文,并且同時(shí)創(chuàng)建一個(gè)全局變量,保證上下文在我們執(zhí)行'增刪改查'的操作過程當(dāng)中不會被銷毀. 代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建上下文
[self createCoreDataContent];
}
創(chuàng)建上下文的方法:
- (IBAction)createCoreDataContent{
//創(chuàng)建數(shù)據(jù)庫文件的路徑
// NSString *path = [NSHomeDirectory() stringByAppendingString:@"Doucments/ImortData"];
// NSFileManager *manager = [NSFileManager defaultManager];
// if (![manager fileExistsAtPath:path]) {
// [manager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
// }
//
//documet目錄下
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [doc stringByAppendingPathComponent:@"/student.db"];//注意不是:stringByAppendingString
NSURL *url = [NSURL fileURLWithPath:path];
NSLog(@"-----------------------------------");
NSLog(@"data : %@",path);
//創(chuàng)建文件,并且打開數(shù)據(jù)庫文件
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
//給存儲器指定存儲的類型
NSError *error;
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
if (store == nil) {
[NSException raise:@"添加數(shù)據(jù)庫錯(cuò)誤" format:@"%@",[error localizedDescription]];
}
//創(chuàng)建圖形上下文
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
context.persistentStoreCoordinator = psc;
self.managedContext = context;
}
第七步,coreData操作-增(插入)
//插入數(shù)據(jù)
- (IBAction)insertData{
NSLog(@"插入數(shù)據(jù)");
//創(chuàng)建模型數(shù)據(jù)模型
Student *student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.managedContext];
student.name = @"張三2";
student.id = @(11);
Book *book = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:self.managedContext];
book.bookID = @(121);
book.bookName = @"<老人與海2>";
student.book = book;
Student *student2 = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.managedContext];
student2.name = @"李四2";
student2.id = @(23);
Book *book2 = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:self.managedContext];
book2.bookID = @(242);
book2.bookName = @"<飛鳥集2>";
student2.book = book2;
//保存,用 save 方法
NSError *error = nil;
BOOL success = [self.managedContext save:&error];
if (!success) {
[NSException raise:@"訪問數(shù)據(jù)庫錯(cuò)誤" format:@"%@",[error localizedDescription]];
}
}
第八步,coreData操作-查
//讀取數(shù)據(jù)庫文件
- (IBAction)readData{
NSLog(@"讀取數(shù)據(jù)");
dispatch_async(dispatch_get_main_queue(), ^{
// 初始化一個(gè)查詢請求
// NSFetchRequest *request = [[NSFetchRequest alloc] init];
// 設(shè)置要查詢的實(shí)體
// request.entity = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:self.managedContext];
//以上代碼簡寫成下邊
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
// 設(shè)置排序(按照age降序)
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"id" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:sort];
// 設(shè)置條件過濾(搜索name中包含字符串"zhang"的記錄锌订,注意:設(shè)置條件過濾時(shí)竹握,數(shù)據(jù)庫SQL語句中的%要用*來代替,所以%Itcast-1%應(yīng)該寫成*zhang*)
// NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*zhang*"];
// request.predicate = predicate;
// 執(zhí)行請求
NSError *error = nil;
NSArray *objs = [self.managedContext executeFetchRequest:request error:&error];
if (error) {
[NSException raise:@"查詢錯(cuò)誤" format:@"%@", [error localizedDescription]];
}
NSLog(@"-----------------------------------");
// 遍歷數(shù)據(jù)
int index = 0;
for (NSManagedObject *obj in objs) {
NSLog(@"%d---name=%@", index++,[obj valueForKey:@"name"]);
}
for (Student *stu in objs) {
Book *book = stu.book;
NSLog(@"%@---name=%@", stu.name,book.bookName);
}
});
}
第九步,coreData操作-改(更新)
//更新數(shù)據(jù)
- (IBAction)modifyData{
// 如果是想做更新操作:只要在更改了實(shí)體對象的屬性后調(diào)用[context save:&error]辆飘,就能將更改的數(shù)據(jù)同步到數(shù)據(jù)庫
//先從數(shù)據(jù)庫中取出所有的數(shù)據(jù),然后從其中選出要修改的那個(gè),進(jìn)行修改,然后保存
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
//設(shè)置過濾條件
NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@",@"李四2"];
request.predicate = pre;
NSError *error = nil;
NSArray *objs = [self.managedContext executeFetchRequest:request error:&error];
if (error) {
[NSException raise:@"查詢錯(cuò)誤" format:@"%@", [error localizedDescription]];
}
// 2.更新身高
for (Student *stu in objs) {
stu.name = @"被修改的新名字";
}
//保存,用 save 方法
BOOL success = [self.managedContext save:&error];
if (!success) {
[NSException raise:@"訪問數(shù)據(jù)庫錯(cuò)誤" format:@"%@",[error localizedDescription]];
}
}
第十步,coreData操作-刪
//刪除
- (IBAction)removeData:(id)sender{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
//查到到你要?jiǎng)h除的數(shù)據(jù)庫中的對象
NSPredicate *predic = [NSPredicate predicateWithFormat:@"name = %@",@"張三2"];
request.predicate = predic;
//請求數(shù)據(jù)
NSArray *objs = [self.managedContext executeFetchRequest:request error:nil];
for (Student *stu in objs) {
[self.managedContext deleteObject:stu];
}
[self.managedContext save:nil];
}
小結(jié)
為了方便地測試上邊的代碼,我們可以把各個(gè)方法在界面上對應(yīng).比如下圖這樣:
到這里,對于coreData的基本操作"增刪改查"的基本操作剛和代碼就完成了.
具體的 Demo 代碼可以在我的 GitHub 上找到 Demo地址
交流
歡迎大家關(guān)注我的微博和我GitHub,我會不時(shí)分享和轉(zhuǎn)發(fā)一些大牛的技術(shù)貼和開源項(xiàng)目.
GitHub:https://github.com/lilongcnc
博客地址:http://www.lilongcnc.cc