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.SQLDebug1。然后在运行程序过程中如果操作了数据库就会将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编辑  收藏  举报

导航