Core Data
Core Data
当前,各类应用开发中只要牵扯到数据库操作通常都会用到一个概念“对象关系映射(ORM)”。无论是哪种平台、哪种技术,ORM框架的作用都是相同的,那就是将关系数据库中的表(准确的说是实体)转换为程序中的对象,其本质还是对数据库的操作(例如Core Data中如果存储类型配置为SQLite则本质还是操作的SQLite数据库)。
iOS中ORM框架首选Core Data,这是官方推荐的,不需要借助第三方框架。Core Data正是为了解决将关系数据库中的表(准确的说是实体)转换为程序中的对象而产生的。
Core Data将数据库的创建、表的创建、对象和表的转换等操作封装起来,简化了我们的操作(注意Core Data只是将对象关系的映射简化了,并不是把服务层替代了,这一点大家需要明白)。
一、概念
1.Core Data 是数据持久化存储的(永久保存)方式
2.数据最终的存储类型可以是:SQLite数据库,XML,二进制,内存里,或自定义数据类型
3.好处:能够合理管理内存,避免使用sql的麻烦,高效(不用写sql语句)
4.构成:
(1)NSManagedObjectContext(被管理的数据上下文)
操作实际内容(操作持久层)
作用:插入数据,查询数据,删除数据
(2)NSManagedObjectModel(被管理的数据模型)
数据库所有表格或数据结构,包含各实体的定义信息
作用:添加实体的属性,建立属性之间的关系
操作方法:视图编辑器,或代码
(3)NSPersistentStoreCoordinator(持久化存储助理)
相当于数据库的连接器
作用:设置数据存储的名字,位置,存储方式,和存储时机
(4)NSManagedObject(被管理的数据记录)
相当于数据库中的表格记录
(5)NSFetchRequest(获取数据的请求)
相当于查询语句
(6)NSEntityDescription(实体结构)
相当于表格结构
(7)后缀为.xcdatamodeld的包
里面是.xcdatamodel文件,用数据模型编辑器编辑
编译后为.momd或.mom文件
5.Core Data几个核心的类:
- Persistent Object Store:可以理解为存储持久对象的数据库(例如SQLite,注意Core Data也支持其他类型的数据存储,例如xml、二进制数据等)。
- Managed Object Model:对象模型,对应Xcode中创建的模型文件。
- Persistent Store Coordinator:对象模型和实体类之间的转换协调器,用于管理不同存储对象的上下文。
- Managed Object Context:对象管理上下文,负责实体对象和数据库之间的交互。
二、Core Data使用
1.创建模型
概览:Data Model 中创建实体和关系 ---> 根据 .xcdatamodeld文件生成具体的实体类 ---> 由实体类创建对象,代码让具体的对象建立关系
使用Core Data进行数据库存取并不需要手动创建数据库,这个过程完全由Core Data框架完成,开发人员面对的是模型,主要的工作就是把模型创建起来,具体数据库如何创建则不用管。
在iOS项目中添加“Data Model”文件。然后在其中创建实体和关系:
模型创建的过程中需要注意:
- 所有的属性应该指定具体类型(尽管在SQLite中可以不指定),因为实体对象会对应生成ObjC模型类。
- 实体对象中其他实体对象类型的属性应该通过Relationships建立,并且注意实体之间的对应关系(一对一、一对多、多对多)(例如一个用户有多条微博,而一条微博则只属于一个用户,用户和微博形成一对多的关系)。
- 通过模型生成类的过程相当简单,通常这些类也不需要手动维护,如果模型发生的变化只要重新生成即可。
以上模型创建后,接下来就是根据上面的模型文件(.xcdatamodeld文件)生成具体的实体类。
在Xcode中添加“NSManagedObject Subclass”文件,按照步骤选择创建的模型及实体,Xcode就会根据所创建模型生成具体的实体类。
生成具体的实体类后,就可以使用这些类建立具体的对象,让对象进行建立关系。
一对一关系:
_app = [UIApplication sharedApplication].delegate;
Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:_app.managedObjectContext];
[person setValue:@"小明" forKey:@"name"];
CardID *card = [NSEntityDescription insertNewObjectForEntityForName:@"CardID" inManagedObjectContext:_app.managedObjectContext];
[card setValue:@"02993939333" forKey:@"cardID"];
//建立关系
[person setValue:card forKey:@"card"];
//[card setValue:person forKey:@"person"];
Person *p = [card valueForKey:@"person"];
NSLog(@"--%@",[p valueForKey:@"name"]);
注意:上面这段代码,因为在创建实体和关系时,实体之间确定了关系是相互关联的反向关系(Inverse)。那么在具体的对象在建立关系时,只要确定一方,另一方自动关联。如上方只需要一个对象 setValue:forKey:,另一个对象不需要。
一对多关系:
#import "ViewController.h" #import "Group.h" #import "Friend.h" #import "AppDelegate.h"
- (void)initModel { AppDelegate *app = [[UIApplication sharedApplication] delegate]; //获取上下文 NSManagedObjectContext *context = app.managedObjectContext; //小张 Friend *f1 = [NSEntityDescription insertNewObjectForEntityForName:@"Friend" inManagedObjectContext:context]; [f1 setValue:@"小张" forKey:@"nickname"]; //小王 Friend *f2 = [NSEntityDescription insertNewObjectForEntityForName:@"Friend" inManagedObjectContext:context]; [f2 setValue:@"小王" forKey:@"nickname"]; //小李 Friend *f3 = [NSEntityDescription insertNewObjectForEntityForName:@"Friend" inManagedObjectContext:context]; [f3 setValue:@"小李" forKey:@"nickname"]; //组1 Group *g1 = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:context]; [g1 setValue:@"我的好友" forKey:@"name"]; //组2 Group *g2 = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:context]; [g2 setValue:@"我的同学" forKey:@"name"]; //建立关系 [g1 addFriendsObject:f1]; [g1 addFriendsObject:f2]; [g2 addFriendsObject:f3]; //保存 [context save:nil]; }
2.创建管理上下文
创建管理上下可以细分为:加载模型文件 -> 指定数据存储路径 -> 创建对应数据类型的存储 -> 创建管理对象上下方并指定存储。
经过这几个步骤之后可以得到管理对象上下文NSManagedObjectContext,以后所有的数据操作都由此对象负责。同时如果是第一次创建上下文,Core Data会自动创建存储文件(例如这里使用SQLite3存储),并且根据模型对象创建对应的表结构。下图为第一次运行生成的数据库及相关映射文件:
为了方便后面使用,NSManagedObjectContext对象可以作为单例或静态属性来保存:
-(NSManagedObjectContext *)createDbContext{
NSManagedObjectContext *context;
//打开模型文件,参数为nil则打开包中所有模型文件并合并成一个
NSManagedObjectModel *model=[NSManagedObjectModel mergedModelFromBundles:nil];
//创建解析器
NSPersistentStoreCoordinator *storeCoordinator=[[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:model];
//创建数据库保存路径
NSString *dir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSLog(@"%@",dir);
NSString *path=[dir stringByAppendingPathComponent:@"myDatabase.db"];
NSURL *url=[NSURL fileURLWithPath:path];
//添加SQLite持久存储到解析器
NSError *error;
[storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
if(error){
NSLog(@"数据库打开失败!错误:%@",error.localizedDescription);
}else{
context=[[NSManagedObjectContext alloc]init];
context.persistentStoreCoordinator=storeCoordinator;
NSLog(@"数据库打开成功!");
}
return context;
}
也可以参考系统自动生成的代码,进行封装。
3.查询数据
对于有条件的查询,在Core Data中是通过谓词来实现的。
条件请求:创建一个请求 --> 设置请求条件 --> 调用上下文执行请求的方法
注意以下几个难点:
- 若有多个条件,使用谓词组合即可;
- 对于关联对象条件,分两种请款查询。
a.查找一个对象只有唯一一个关联对象的情况,例如查找用户名为“Binger”的微博(一个微博只能属于一个用户),通过keypath查询
-(NSArray *)getStatusesByUserName:(NSString *)name{
NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@"Status"];
request.predicate=[NSPredicate predicateWithFormat:@"user.name=%@",name];
NSArray *array=[self.context executeFetchRequest:request error:nil];
return array;
}
b.查找一个对象有多个关联对象的情况,此时可以充分利用谓词进行过滤
例如QQ中,查找「'小王'所在的组」和「组2(我的同学)中的所有的好友」:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
AppDelegate *app = [[UIApplication sharedApplication] delegate];
//获取上下文
NSManagedObjectContext *context = app.managedObjectContext;//1.找小王对应的组
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Friend"];
//查找小王
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"nickname='小王'"];
request.predicate = predicate;
NSArray *array = [context executeFetchRequest:request error:nil];
for (Friend *xiaowang in array)
{
//取出名字为小王对应的组
Group *group = [xiaowang valueForKey:@"group"];
NSLog(@"%@",[group valueForKey:@"name"]);
}
//2.找组2(我的同学)对应的好友
NSLog(@"------------------------------");
request = [NSFetchRequest fetchRequestWithEntityName:@"Group"];
predicate = [NSPredicate predicateWithFormat:@"name='我的同学'"];
//设置条件
request.predicate = predicate;
array = [context executeFetchRequest:request error:nil];
for (Group *group in array)
{
//遍历当前组对应的好友
NSSet *friends = [group valueForKey:@"friends"];
for (Friend *f in friends)
{
NSLog(@"%@",[f valueForKey:@"nickname"]);
}
}
}
又例如查找发送微博内容中包含“Watch”并且用户昵称为“小娜”的用户(一个用户有多条微博),此时可以充分利用谓词进行过滤:
-(NSArray *)getUsersByStatusText:(NSString *)text screenName:(NSString *)screenName{
NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@"Status"];
request.predicate=[NSPredicate predicateWithFormat:@"text LIKE '*Watch*'",text];
NSArray *statuses=[self.context executeFetchRequest:request error:nil];
NSPredicate *userPredicate= [NSPredicate predicateWithFormat:@"user.screenName=%@",screenName];
NSArray *users= [statuses filteredArrayUsingPredicate:userPredicate];
return users;
}
4.增、删、改
注意,增、删、改操作完最后必须调用管理对象上下文的保存方法,否则操作不会执行。
增:插入数据需要调用实体描述对象NSEntityDescription返回一个实体对象,然后设置对象属性,最后保存当前上下文即可。
删:删除数据可以直接调用管理对象上下文的deleteObject方法,删除完保存上下文即可。注意,删除数据前必须先查询到对应对象。
改:修改数据首先是取出对应的实体对象(通过查),然后通过修改对象的属性,最后保存上下文。
5.实体间的 Delete Rules
当你删除对象时,你在Xcode内置的数据建模器中为实体的关系设定的删除规则(Delete Rules),如图4.2所示。指明了关系会受到怎样的影响:
- 如果关系的删除规则设定为Nullify(作废)(默认)。当A对象的关系指向的B对象被删除后,A对象的关系将被设为nil。对于To Many关系类型,B对象只会从A对象的关系的容器中被移除。
- 如果关系的删除规则为Cascade(级联),当B对象的关系指向的C对象被删除后,B对象也会被删除。B对象关联(以Cascade删除规则)的二级对象A也会被删除。以此类推。
- 如果关系的删除规则为Deny(拒绝),如果删除A对象时,A对象的关系指向的B对象仍存在,则删除操作会被拒绝。
- 如果关系的删除规则为NO Action,当A对象的关系指向的B对象被删除后(A->B),A对象保持不变,这意味着A对象的关系会指向一个不存在的对象。如果没有充分的理由,最好不要使用。
三、调试
虽然Core Data(如果使用SQLite数据库)操作最终转换为SQL操作,但是调试起来却不像操作SQL那么方便。特别是对于初学者而言经常出现查询报错的问题,如果能看到最终生成的SQL语句自然对于调试很有帮助。事实上在Xcode中是支持Core Data调试的,具体操作:Product-Scheme-Edit Scheme-Run-Arguments中依次添加两个参数(注意参数顺序不能错):-com.apple.CoreData.SQLDebug、1。然后在运行程序过程中如果操作了数据库就会将SQL语句打印在输出面板。
四、CoreData数据库升级和版本迁移
如果IOS App 使用到CoreData,并且在上一个版本上有数据库更新(新增表、字段等操作),那在覆盖安装程序时就要进行CoreData数据库的迁移,具体操作如下:
1.选中你的mydata.xcdatamodeld文件,选择菜单editor->Add Model Version 比如取名:mydata2.xcdatamodel
2.设置当前版本
选择上级mydata.xcdatamodeld ,在inspector中的Versioned Core Data Model选择Current模版为mydata2
3.修改新数据模型mydata2,在新的文件上添加字段及表
4.删除原来的类文件,重新生成下类。
5.在persistentStoreCoordinator中添加
添加 *optionsDictionary,原来options:nil 改成options:optionsDictionary
6.重新编译下程序。
本文部分图文内容转载自崔江涛(KenshinCui),iOS开发系列--数据存取。
posted on 2016-03-23 19:55 Wilson_CYS 阅读(262) 评论(0) 编辑 收藏 举报