在真实开发中,因为需求是不断变化的,说不定什么时候就需要往模型里添加新的字段,添加新的模型,甚至是大规模的重构;所以数据的迁移就显得尤为重要了。
CoreData 中,数据迁移本质就是把旧的 SQLite 数据库里的内容,复制到新的 SQLite 数据库里去,让新的数据库作为默认的数据存储。伴随着模型版本的变化,新旧两个数据库的实体结构当然也是不同的。这就是说在迁移过程中必须知道新旧两个数据库的模型对应关系,旧数据库里的数据该怎么复制到新的数据库中。这在 CoreData 中是由 MappingModel 映射模型来决定的。我们所需要做的就是创建 MappingModel 文件,指定好实体不同版本间的映射,CoreData 就会自动帮我们完成数据迁移。当然如果模型版本的变化比较小,CoreData 是可以自动推断出映射模型的。下面就来详细的介绍一下 CoreData 里常用的几种迁移。

创建模型版本

在介绍数据迁移之前,先来看如何创建新的模型版本,在 Xcode 里模型是通过 .xcdatamodeld 文件来创建的,实际上这个文件就是一个包,里面可以包含不同的模型版本。选中这个文件,然后点击 Editor->Add Model Version... 就可以添加一个新的模型版本。

add-model-version-w400

然后会弹出下面这个对话框,默认的新的模型会在原来的基础上增加一个数字,来标识不同的模型版本。这个数字也是可以更改的,你可以按照自己的喜好更改成 v2 或者其他的。

version-name-w600

点击 finish 后就会看到现在的 LearnCoreData.xcdatamodeld文件可以展开了,里面包含了所有的模型版本文件,它们是 xcdatamodel 格式的。在右侧的 File Inspector 面板中可以指定当前的模型版本,然后程序打包后就会把选中的模型版本作为当前的默认版本。

model-version-w300

自动推断映射模型

上面说到对于一些较小的变化,CoreData 是可以自动推断映射模型的,从而帮助我们自动地完成数据迁移。针对下面这些改动,CoreData 都可以自动的进行推断:

  • 添加一个属性
  • 移除一个属性
  • 非空的属性变成可以为空的
  • 可以为空的属性变成非空属性并设置一个默认值
  • 重命名实体或者属性(需要设置 renaming identifier)
  • 添加/删除 RelationShip
  • 重命名 RelationShip(需要设置 renaming identifier)
  • 把一个 RelationShip 从 对一改成对多,或者把非排序的改成排序的。(反过来也是可以的)

上面说到的 renaming identifier 可以在 Model Inspector 进行设置,对不同版本的对应实体/属性设置相同的 Renaming ID,CoreData 就可以自动推断出对应的映射模型。

renaming-identifier-w600

除此之外,在向 persistentStoreCoordinator 调用 addPersistentStoreWithType:configuration:URL:options:error: 添加 persistentStore时,需要将 optionsNSMigratePersistentStoresAutomaticallyOptionNSInferMappingModelAutomaticallyOption 两个 key 设置为 YES,CoreData 才会自动推断。

注意 :这里的renamingid何时使用呢?就是如果说原来的字段比如叫做A, 新的数据库想把名字改为B,但是值还是之前的,那么就需要在新的数据库中设置这个renamingid的值,如果原来的对应的字段没有设置renamingid,那么默认就需要在新的数据库字段的renamingid一栏写成原来数据库对应的字段的名字。如果原来的字段也设置了renamingid,那么就需要在新的里面也要写上这个renamingid,即新的数据库和旧的数据库同一字段的renamingid也一致,才能达到只改字段名字的效果。

下面我们来看一下,怎么使用自动推断。这是初始版本的 StudentEntity 实体的结构:

StudentEntity-1-w600

下面我们再创建一个 Model Version,把原来的 StudentEntity、ClassEntity、CourseEntity 的 EntityName 分别修改成 Student、Clazz、Course;Student 里面的字段修改成 name、id 和 age,另外再添加一个 BOOL 字段 sex,表示性别,默认值设置为 YES。

StudentEntity-2-w600

然后为两个版本中修改过的实体名字和属性字段名字设置相同的 renaming identifier。以 Student 的 name 字段为例,旧版的模型中:

studentName-RenamingID-w600

然后新版本的模型中:

new-name-w600

修改好后,暂时我们先不切换到新版本的模型中,先用旧的数据库生成一些测试数据,然后在沙盒的 Library/Application Support/ 目录里复制出里边的三个文件,然后用 SQLite 工具打开 .sqlite 的数据库文件查看数据库的的结构,和刚存进去的内容。

sqlite-w600

这是打开后的 StudentEntity 表,里面随机插入了 300 条数据,注意到现在由我们创建的几个字段分别是 ZSTUDENTID、ZSTUDENTCLASS、ZSTUDENTNAME。

StudentEntity-v1-w600

现在我们把数据库切换到新版中,然后再运行一次程序,重新打开新生成的数据库文件,就会看到新版的数据库的结构:

StudentEntity-v2-w600

现在 StudentEntity 已经变成了 Student,每个字段也都变成了新的字段名,而且里面也多了我们添加的 sex 字段。这就说明 CoreData 的自动推断成功了。

自定义映射模型

大多数情况下自动推断就能帮我们完成数据的迁移,但当数据的变化更复杂时,例如如果我们把 Student 里的一个字段提取出来放到一个新的字段中去。就得靠我们手动创建 mapping model 了。例如我们现在想把上面 Clazz 表删除,原来的 Student 中的 clazz 字段用 clazzName 字段来代替。那么这种情况下就需要手动来创建 mapping model 了。
在这之前我们先用旧版的数据模型插入一些示例的数据,这是插入的 Student 数据:

Student-data-w600

Clazz 数据:

Clazz-data-w600

Course 数据:

Course-data-w600

因为 Course 和 Student 是多对多的关系,所以还会有一张关联表:

SCoursesStudents-data-w600

这是插入示例数据的代码:

  1. - (void)insertManyStudents {
  2. NSSet *science = [self scienceCourses];
  3. NSSet *art = [self artCourses];
  4. Clazz *clazz1 = [[Clazz alloc] initWithContext:self.persistentContainer.viewContext];
  5. clazz1.clazzName = @"文科一班";
  6. clazz1.classId = 1;
  7. Clazz *clazz2 = [[Clazz alloc] initWithContext:self.persistentContainer.viewContext];
  8. clazz2.clazzName = @"理科一班";
  9. clazz2.classId = 2;
  10. for (NSUInteger i = 0; i < 300; i++) {
  11. NSString *name = [NSString stringWithFormat:@"student-%u", arc4random_uniform(100000)];
  12. int16_t age = (int16_t)arc4random_uniform(10) + 10;
  13. int16_t stuId = (int16_t)arc4random_uniform(INT16_MAX);
  14. Student *student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.persistentContainer.viewContext];
  15. student.name = name;
  16. student.age = age;
  17. student.id = stuId;
  18. if (i % 2 == 0) {
  19. student.clazz = clazz1;
  20. student.courses = art;
  21. } else {
  22. student.clazz = clazz2;
  23. student.courses = science;
  24. }
  25. }
  26. NSError *error;
  27. [self.persistentContainer.viewContext save:&error];
  28. }
  29. - (NSSet<Course *> *)scienceCourses {
  30. Course *physics = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  31. physics.courseName = @"物理";
  32. physics.courseId = 1;
  33. physics.courseChapterCount = 5;
  34. Course *chemistry = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  35. chemistry.courseName = @"化学";
  36. chemistry.courseId = 2;
  37. chemistry.courseChapterCount = 9;
  38. Course *biology = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  39. biology.courseName = @"生物";
  40. biology.courseId = 3;
  41. biology.courseChapterCount = 10;
  42. NSSet *courses = [NSSet setWithObjects:physics, chemistry, biology, nil];
  43. return courses;
  44. }
  45. - (NSSet<Course *> *)artCourses {
  46. Course *chinese = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  47. chinese.courseName = @"语文";
  48. chinese.courseId = 4;
  49. chinese.courseChapterCount = 12;
  50. Course *history = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  51. history.courseName = @"历史";
  52. history.courseId = 5;
  53. history.courseChapterCount = 19;
  54. Course *geography = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  55. geography.courseName = @"地理";
  56. geography.courseId = 6;
  57. geography.courseChapterCount = 21;
  58. return [NSSet setWithObjects:chinese, geography, history, nil];
  59. }

然后我们再来看一下 新创建的 v3 版本的数据模型的结构:
Student 表

Student-table-w600

Course 表

Course-table-w600

这一次我们不再创建 Clazz 表了,因为它要被 Student 表里的 clazzName 字段代替。

接下来创建 Mapping Model 文件

Mapping-Model-w600

创建过程中需要选择 Source data model 和 Destination data model,也就是迁移的旧版和新版数据模型版本,分别选择 v2 和 v3 版本:

Source-data-model-w600

Target-data-model-w600

最后保存的文件名建议按一定的规则来命名,后期也方便查找:

Save-mapping-model-w600

然后我们来认识一下 mapping model 的用法,创建好后,mapping model 还是会自动推断出大多数的字段映射,例如 Student 表中除新添加的 clazzName 字段外,其他的都可以正确的推断出来;

StudentToStudent-mapping-w600

当然,如果字段名修改过的话,同样是不能推断出来的,如 Course 表的字段:

CourseToCourse-mapping-w600

另外每个 Entity Mapping 的名字的命名规则是以 SourceEntityNameToDestinationEntityName 来命名的,这个可以在右侧的面板中修改:

Entity-Mapping-name-w600

下面来介绍 mapping model 中会用到的几个对象:

  • $source - 对应着 NSMigrationSourceObjectKey,可以理解为 Source Model 的一个实体对象
  • $manager - 对应着 NSMigrationManagerKey,它代表的是 NSMigrationManager 对象,正是这个对象在迁移过程中发挥着作用,它管理着源对象和目标对象之间的关联

除了这两个,还有几个不常用的:

  • $destination -- NSMigrationDestinationObjectKey
  • $entityMapping -- NSMigrationEntityMappingKey
  • $propertyMapping -- NSMigrationPropertyMappingKey
  • $entityPolicy -- NSMigrationEntityPolicyKey

在 mapping model 中可以通过 \$ 加对应的名字,直接访问这几个对象。例如上面图中 \$source.name 就代表源对象的 name 属性。同样的我们就可以把其他未推断出来的填上:

-w600

-w600

然后再来看 Relationship Mapping 映射:

对于这种关联到外部表的字段,相对于普通字段会复杂一些,我们需要通过右侧的面板来进行配置,Name 代表 RelationShip 的字段名;Key Path 代表这个字段对应的源对象上的字段,对于 courses 来说就是 $source.courses;然后是 Mapping Name,它代表这个 RelationShip 所关联的外部表的 Entity Mapping,对于 courses 来说就是 Course 的 Entity Mapping 也就是 CourseToCourse。配置好这些后,Xcode 会生成一段长长的 Value Expression 表达式:

FUNCTION($manager, "destinationInstancesForEntityMappingNamed:sourceInstances:" , "CourseToCourse", $source.courses)

意思就是调用 $manager 对象的 destinationInstancesForEntityMappingNamed:sourceInstances: 方法 CourseToCourse\$source.courses 分别是两个传入参数。 它会根据 CourseToCourse 的映射规则生成$source.courses 的目标对象。
同样的,我们可以据此来配置 Course 里的 students 关系:

-w600

所有字段都配置完后,就可以把 模型版本切换都 v3 然后运行程序。程序在运行时发现当前的 v3 版本数据模型和本地存储的 v2 数据库版本不一致,就会自动从 bundle 里寻找对应 v2 到 v3 的 Mapping Model,依据自定义的 Mapping Model,数据就会自动迁移完成。
下面来看一下迁移完成的 v3 版本数据库。
Student 表:

Student-table-v3-w600

Course 表:

Course-table-v3-w600

自动生成的 Course 和 Student 之间的关联表:

Students-table-v3-w600

可以看到 Student 表中的 clazz 字段已经被 clazzName 替换了。同时其他的数据也都没有丢失。

转载于:https://www.cnblogs.com/Free-Thinker/p/9841965.html

coredata 数据库升级相关推荐

  1. 成为MySQL DBA 博客系列-数据库升级

    数据库供应商通常每个月都会发布一些有bug /安全性修补程序的补丁,我们为什么要关心?新的版本可能对关于安全漏洞和黑客入侵系统的修复,所以除非安全性不被关注,否则您可能希望在您的系统上安装最新的安全修 ...

  2. 18.移动架构数据库升级解决方案

    今天的数据库升级将通过这个脚本文件完成,我们假设目前版本迭代中,已经上线了V001 V002版本,近期将要上线V003.首先先理清三个版本的数据库情况,三个版本中都涉及一个个数据库文件(暂不考虑分库的 ...

  3. 关于开发人员数据库权限配置以及规范数据库升级流程

    在项目的新版本发布过程中,暴露出了一些数据库权限管理的问题和数据库升级流程规范的问题. 在这次发布完真实版后,居然发现有些脚本没有被执行,导致用户升级完就出现了些Bug,产生了很不好的影响. 为此项目 ...

  4. EBS 11i数据库升级(9i-10g)几点事项

    最近几日在评估和测试EBS的系统升级(11i升级到R12),虽然官方的升级文档里介绍的比较详细了,但是依旧会出现一些容易疏忽的问题,这里做一些记录.这里并不是单纯的数据库升级,需要考虑EBS的特殊应用 ...

  5. 探索Oracle之数据库升级八 12c Downgrade 11gR2

    探索Oracle之数据库升级八 12c Downgrade 11gR2 前言: 我们前面已经完毕了11gR2 upgrade to 12c 的升级,整个过程还是比較顺利的,尽管和曾经版本号升级有些不太 ...

  6. Android 数据库升级解决方案

    请考虑如下情况: 在数据库升级时,不同版本的数据库,他们定义的表结构完全可能是不一样的,比如V1.0的表A有10个column,而在V1.1的表A有12个colum,在升级时,表A增加了两列,此时我们 ...

  7. Oracle数据库升级与补丁

    以下是Maclean.Liu 编写或收集的数据库升级(Upgrade)与补丁(patch fix)方面的知识: 甲骨文发布2012 1月数据库安全补丁Critical Patch Update Jan ...

  8. 数据库升级后,准备使用原有数据文件启动数据库

    因听到以前同事说到11.2.1版本在进行子查询时,速度有时会莫名的慢下来.正好有时间,也是测试服务器, 那就把数据库升级到11.2.3吧.原计划是升级后可以直接使用数据库的相关文件就能启动数据库. 但 ...

  9. Android数据库升级实例

    第一部分 Andoird的SQLiteOpenHelper类中有一个onUpgrade方法.帮助文档中只是说当数据库升级时该方法被触发.经过实践,解决了我一连串的疑问: 1. 帮助文档里说的" ...

最新文章

  1. 在Yolov5 Yolov4 Yolov3 TensorRT 实现Implementation
  2. couldn't register *** with the bootstrap server. Error: unknown error code.
  3. 关于MVVM与MVC
  4. 会计的疑惑--BigDecimal的秘密
  5. Acess link
  6. 如何用Snapgene 4.3.6进行序列比对及查找碱基位点
  7. 机器学习三要素之策略
  8. Android开发之设置Edittext小数点后两位以及限制位数同时使用
  9. 链表操作时头结点的好处
  10. 由最小生成树算法改到最短路径算法代码----为了区分两者的区别
  11. 求你了,别再说Java对象都是在堆内存上分配空间的了!
  12. linux 查看tdagent进程,Zabbix监控记录linux服务器近期的登录情况
  13. mysql 时间_MySQL 日期时间
  14. json爬虫获取列表数据不全,已解决
  15. micropython文件上传软件_4-3 为NodeMCU刷入MicroPython固件
  16. Process Explorer 诊断和排错实例(下)
  17. 最实诚导师招生简章“火”了:目前只有不太充足的经费,但绝不克扣研究生补贴!...
  18. 密钥分发中心KDC 对称密钥分发中心 P112
  19. 通过PreparedStatement执行更新查询操作
  20. c语言setlocale第二个参数,setlocale_本地化支持 | Localization support_C_参考手册_非常教程...

热门文章

  1. codeforces 706B B. Interesting drink(二分)
  2. sql server 海量数据速度提升:SQL优化-索引(7) 【转】
  3. Linux下的OpenSSL编程
  4. NoSQL(2)之 Redis配置与优化
  5. python代码规范化_最流行的Python代码规范
  6. 开放报名 | 阿里云 Serverless 研修班走进四川大学
  7. 权威认可!PolarDB-X高分通过中国信通院《分布式事务数据库稳定性专项评测》
  8. 阿里云弹性计算产品负责人:让客户用上本地化的公共云
  9. 工作和人工智能的未来
  10. 单元测试,只是测试吗?