一.文章概要
当前使用工具是XCode7.
这篇文章主要是写了对于基本数据类型的"增删改查"的操作,至于特殊类型比如UIImage,NSArray,NDDictionary我们在之后的几篇文章中讲到.
二.前话
从平常的使用来说,FMDB和coreData是项目中最经常使用的两个数据库持久化方式.
CoreData 首先它是一个 ORM框架。 SQLite 与 FMDB 其实都脱离不了关系型(relational)数据 而我们平时使用的编程语言却是面向对象的,都是对象型的。
在 iOS 中,coreData 和 FMDB 关于这两者的区别,我认为主要是如下几个方面
coreData
优点
(1)映射之后 直接操作对象就能进行增删改查 更贴近 程序员的生活。
(2)我们不需要再写SQL语句,避免了 SQL语句的繁琐,比如说 SQL语句经常要写一大堆的包含 values(name,sex) 还要有对应个数的?与前边的()中的属性个数对应
缺点
(1) coreData没有 java 中的 ORM 框架快速.尤其是多表操作的时候,效率并不是很高. java中的框架(Hibernate:特点:可以把 SQL语句和面型对象的查询语句混合使用.举个例子:它可以把复杂的多表操作语句用一行查询语句来执行完毕)。而 coreData操作多表的时候,需要创建多个对应数量的Requset,description,谓词,筛选器。
(2)就单纯的批量插入数据的速度 相比较FMDB 稍微慢一点点
三.FMDB
如果你之前用过sqlSever数据库的话,继续往下看,当然你也可以直接跳过这一小节
首先,想必软件工程专业或者相关专业的同学在学校学的就是sqlSever,对应的数据库课程也是免不了的.那么sql语句是我们必须要学习的."简单查询""多表查询"等等这些都是我们要学习的.恭喜你,你已经有了一定学习FMDB的基础了.
再进一步说,你之前从事java或者安卓开发工作的时候,用到了sqlSever,那么再次恭喜你,你基本已经可以FMDB的那些基本功能了.
由此,我们可见,FMDB的使用方式和之前在其他语言中使用sqlSever数据库的时候,是非常相似的.之前我们在使用sqlSever提到的那些关键点在FMDB中,也有体现,比如"打开数据库,关闭数据库""手敲sql语句,执行sql语句"......所以,FMDB,对于之前上手还是很熟悉,很简单的.
你到现在还没有使用过FMDB. 那也不用太烦恼和害怕,数据库的基本格式都是固定的.既然是固定的,拷贝一段改改就可以了.
扩展
(1)问:"什么时候使用coredata 什么时候使用FMDatabases?"
答:
a. CoreData 在公司使用的比较少,用户的比较多的是FMDatabases。
b.数据存储的结构比较简单的时候,使用CoreData
c.CoreData 开发效率会高点,为什么?因为他是面向对象的,而且不用写sql语句.
FMDatabases 数据结果比较复杂的时候,表与表之间的关联比较多的时候使用.
(2)coreData 其实底层也是要写sql 语句的,coreData 帮我们把sql语句封装。
四.你要的干货,利用coreData来持久化数据
第一步,创建'模型文件'
第二步,创建'实体Entity'
创建实体对象
创建实体对象的属性(属性名,属性类型和关联实体)
问:什么是关联实体???
答:其实就是我们创建另外的一个实体.这里添加关联实体的意思就是,举个例子我们人类有个宠物狗,那么反过来宠物狗也有个主人是人类. 这里说白了就是两个对象之间的相互关联
温馨提示
第三步,创建'创建NSManagedObject的子类文件'
以下的代码是系统默认生成的,这里贴出是仅供对比查验使用,在文篇文章中没有其它的意义.
创建出的子类代码-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
创建出的子类代码-Book+CoreDataProperties.m:
#import "Book+CoreDataProperties.h"
@implementation Book (CoreDataProperties)
@dynamic bookID;
@dynamic bookName;
@dynamic student;
@end
创建出的子类代码-Book.h:
#import
#import
@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"
创建出的子类代码-Book.m:
#import "Book.h"
#import "Student.h"
@implementation Book
// Insert code here to add functionality to your managed object subclass
@end
创建出的子类代码-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
创建出的子类代码-Student+CoreDataProperties.m:
#import "Student+CoreDataProperties.h"
@implementation Student (CoreDataProperties)
@dynamic id;
@dynamic name;
@dynamic book;
@end
创建出的子类代码-Student.h:
#import
#import
@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"
创建出的子类代码-Student.m:
#import "Student.h"
#import "Book.h"
@implementation Student
// Insert code here to add functionality to your managed object subclass
@end
第四步,创建'coreData的管理类'
我们暂时不做封装,直接在UIViewController演示.
第五步,coreData操作-创建(获取)上下文
这里的上下文在OC中,类似我们coreAnimation中用到的图形上下文. OC中哪些有上下文?
在正式看下边的'增删改查'代码之前,我们需要也别注意的是:
(0)在我们每次执行四个操作的时候,都首先要保证上下文存在(还活着)
(1)并且代码执行在'异步主队列中'
(2)'增'部分:创建保存对象部分,不是直接new或者alloc-init
(3)'查'部分:NSPredicate过滤条件(精确和非精确)
(4)'改'部分:'查'部分:NSPredicate过滤条件(精确和非精确)
(5)'删'部分:'查'部分:NSPredicate过滤条件(精确和非精确)
第六步,coreData操作-创建上下文
上边提到我们的演示文件类型是UIViewController,所以是在'viewDidLoad'方法中创建上下文,并且同时创建一个全局变量,保证上下文在我们执行'增删改查'的操作过程当中不会被销毁. 代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
//创建上下文
[self createCoreDataContent];
}
创建上下文的方法:
- (IBAction)createCoreDataContent{
//创建数据库文件的路径
// 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);
//创建文件,并且打开数据库文件
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:@"添加数据库错误" format:@"%@",[error localizedDescription]];
}
//创建图形上下文
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
context.persistentStoreCoordinator = psc;
self.managedContext = context;
}
第七步,coreData操作-增(插入)
//插入数据
- (IBAction)insertData{
NSLog(@"插入数据");
//创建模型数据模型
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:@"访问数据库错误" format:@"%@",[error localizedDescription]];
}
}
第八步,coreData操作-查
//读取数据库文件
- (IBAction)readData{
NSLog(@"读取数据");
dispatch_async(dispatch_get_main_queue(), ^{
// 初始化一个查询请求
// NSFetchRequest *request = [[NSFetchRequest alloc] init];
// 设置要查询的实体
// request.entity = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:self.managedContext];
//以上代码简写成下边
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
// 设置排序(按照age降序)
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"id" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:sort];
// 设置条件过滤(搜索name中包含字符串"zhang"的记录,注意:设置条件过滤时,数据库SQL语句中的%要用*来代替,所以%Itcast-1%应该写成*zhang*)
// NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*zhang*"];
// request.predicate = predicate;
// 执行请求
NSError *error = nil;
NSArray *objs = [self.managedContext executeFetchRequest:request error:&error];
if (error) {
[NSException raise:@"查询错误" format:@"%@", [error localizedDescription]];
}
NSLog(@"-----------------------------------");
// 遍历数据
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操作-改(更新)
//更新数据
- (IBAction)modifyData{
// 如果是想做更新操作:只要在更改了实体对象的属性后调用[context save:&error],就能将更改的数据同步到数据库
//先从数据库中取出所有的数据,然后从其中选出要修改的那个,进行修改,然后保存
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
//设置过滤条件
NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@",@"李四2"];
request.predicate = pre;
NSError *error = nil;
NSArray *objs = [self.managedContext executeFetchRequest:request error:&error];
if (error) {
[NSException raise:@"查询错误" format:@"%@", [error localizedDescription]];
}
// 2.更新身高
for (Student *stu in objs) {
stu.name = @"被修改的新名字";
}
//保存,用 save 方法
BOOL success = [self.managedContext save:&error];
if (!success) {
[NSException raise:@"访问数据库错误" format:@"%@",[error localizedDescription]];
}
}
第十步,coreData操作-删
//删除
- (IBAction)removeData:(id)sender{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
//查到到你要删除的数据库中的对象
NSPredicate *predic = [NSPredicate predicateWithFormat:@"name = %@",@"张三2"];
request.predicate = predic;
//请求数据
NSArray *objs = [self.managedContext executeFetchRequest:request error:nil];
for (Student *stu in objs) {
[self.managedContext deleteObject:stu];
}
[self.managedContext save:nil];
}
小结
为了方便地测试上边的代码,我们可以把各个方法在界面上对应.比如下图这样:
到这里,对于coreData的基本操作"增删改查"的基本操作刚和代码就完成了.
具体的 Demo 代码可以在我的 GitHub 上找到 Demo地址
交流
欢迎大家关注我的微博和我GitHub,我会不时分享和转发一些大牛的技术贴和开源项目.
GitHub:https://github.com/lilongcnc
博客地址:http://www.lilongcnc.cc