当前位置: 首页 > news >正文

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开发系列--数据存取。

转载于:https://www.cnblogs.com/chenyuansheng/p/5312623.html

相关文章:

  • c++两个类相互调用需要注意的问题
  • sizeof的主要用法
  • 多线程在VC下和linux下的应用
  • js中substring和substr的用法比较
  • 理解JavaScript定时器:setTimeout和setInterval
  • 13、jQueryMobile知识总结
  • 控件函数对话框上的控件的大小和位置随着对话框的大小的改变而变化
  • 线段树(多棵) HDOJ 4288 Coder
  • linux基础1
  • windons 安装ruby on rails
  • ChannelHandler adapters
  • 设置MySQL开机自动启动
  • [svc][op]关闭linux centos各种声音
  • unity3d倒计时后几秒改变颜色方法
  • js 多语言转换代码
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 【css3】浏览器内核及其兼容性
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • Android Volley源码解析
  • Apache Pulsar 2.1 重磅发布
  • Intervention/image 图片处理扩展包的安装和使用
  • Less 日常用法
  • Linux后台研发超实用命令总结
  • Logstash 参考指南(目录)
  • MobX
  • PHP面试之三:MySQL数据库
  • Python_OOP
  • Python十分钟制作属于你自己的个性logo
  • Vue全家桶实现一个Web App
  • 包装类对象
  • 技术胖1-4季视频复习— (看视频笔记)
  • 聊聊flink的BlobWriter
  • 前端学习笔记之观察者模式
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 手写一个CommonJS打包工具(一)
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • (52)只出现一次的数字III
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (动态规划)5. 最长回文子串 java解决
  • (二)JAVA使用POI操作excel
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (一)u-boot-nand.bin的下载
  • (转)c++ std::pair 与 std::make
  • (转)Sql Server 保留几位小数的两种做法
  • .NET BackgroundWorker
  • .Net CF下精确的计时器
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • .Net 知识杂记
  • .NET 中的轻量级线程安全
  • .Net7 环境安装配置