日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

coredata 数据库升级

發(fā)布時間:2024/8/26 数据库 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 coredata 数据库升级 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在真實開發(fā)中,因為需求是不斷變化的,說不定什么時候就需要往模型里添加新的字段,添加新的模型,甚至是大規(guī)模的重構;所以數(shù)據(jù)的遷移就顯得尤為重要了。
CoreData 中,數(shù)據(jù)遷移本質就是把舊的 SQLite 數(shù)據(jù)庫里的內容,復制到新的 SQLite 數(shù)據(jù)庫里去,讓新的數(shù)據(jù)庫作為默認的數(shù)據(jù)存儲。伴隨著模型版本的變化,新舊兩個數(shù)據(jù)庫的實體結構當然也是不同的。這就是說在遷移過程中必須知道新舊兩個數(shù)據(jù)庫的模型對應關系,舊數(shù)據(jù)庫里的數(shù)據(jù)該怎么復制到新的數(shù)據(jù)庫中。這在 CoreData 中是由 MappingModel 映射模型來決定的。我們所需要做的就是創(chuàng)建 MappingModel 文件,指定好實體不同版本間的映射,CoreData 就會自動幫我們完成數(shù)據(jù)遷移。當然如果模型版本的變化比較小,CoreData 是可以自動推斷出映射模型的。下面就來詳細的介紹一下 CoreData 里常用的幾種遷移。

創(chuàng)建模型版本

在介紹數(shù)據(jù)遷移之前,先來看如何創(chuàng)建新的模型版本,在 Xcode 里模型是通過 .xcdatamodeld 文件來創(chuàng)建的,實際上這個文件就是一個包,里面可以包含不同的模型版本。選中這個文件,然后點擊 Editor->Add Model Version... 就可以添加一個新的模型版本。


add-model-version-w400


然后會彈出下面這個對話框,默認的新的模型會在原來的基礎上增加一個數(shù)字,來標識不同的模型版本。這個數(shù)字也是可以更改的,你可以按照自己的喜好更改成 v2 或者其他的。


version-name-w600

點擊 finish 后就會看到現(xiàn)在的 LearnCoreData.xcdatamodeld文件可以展開了,里面包含了所有的模型版本文件,它們是 xcdatamodel 格式的。在右側的 File Inspector 面板中可以指定當前的模型版本,然后程序打包后就會把選中的模型版本作為當前的默認版本。


model-version-w300

自動推斷映射模型

上面說到對于一些較小的變化,CoreData 是可以自動推斷映射模型的,從而幫助我們自動地完成數(shù)據(jù)遷移。針對下面這些改動,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時,需要將 options 的 NSMigratePersistentStoresAutomaticallyOption 和 NSInferMappingModelAutomaticallyOption 兩個 key 設置為 YES,CoreData 才會自動推斷。

注意 :這里的renamingid何時使用呢?就是如果說原來的字段比如叫做A, 新的數(shù)據(jù)庫想把名字改為B,但是值還是之前的,那么就需要在新的數(shù)據(jù)庫中設置這個renamingid的值,如果原來的對應的字段沒有設置renamingid,那么默認就需要在新的數(shù)據(jù)庫字段的renamingid一欄寫成原來數(shù)據(jù)庫對應的字段的名字。如果原來的字段也設置了renamingid,那么就需要在新的里面也要寫上這個renamingid,即新的數(shù)據(jù)庫和舊的數(shù)據(jù)庫同一字段的renamingid也一致,才能達到只改字段名字的效果。

下面我們來看一下,怎么使用自動推斷。這是初始版本的 StudentEntity 實體的結構:


StudentEntity-1-w600

下面我們再創(chuàng)建一個 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

修改好后,暫時我們先不切換到新版本的模型中,先用舊的數(shù)據(jù)庫生成一些測試數(shù)據(jù),然后在沙盒的 Library/Application Support/ 目錄里復制出里邊的三個文件,然后用 SQLite 工具打開 .sqlite 的數(shù)據(jù)庫文件查看數(shù)據(jù)庫的的結構,和剛存進去的內容。


sqlite-w600

這是打開后的 StudentEntity 表,里面隨機插入了 300 條數(shù)據(jù),注意到現(xiàn)在由我們創(chuàng)建的幾個字段分別是 ZSTUDENTID、ZSTUDENTCLASS、ZSTUDENTNAME。


StudentEntity-v1-w600

現(xiàn)在我們把數(shù)據(jù)庫切換到新版中,然后再運行一次程序,重新打開新生成的數(shù)據(jù)庫文件,就會看到新版的數(shù)據(jù)庫的結構:


StudentEntity-v2-w600

現(xiàn)在 StudentEntity 已經變成了 Student,每個字段也都變成了新的字段名,而且里面也多了我們添加的 sex 字段。這就說明 CoreData 的自動推斷成功了。

自定義映射模型

大多數(shù)情況下自動推斷就能幫我們完成數(shù)據(jù)的遷移,但當數(shù)據(jù)的變化更復雜時,例如如果我們把 Student 里的一個字段提取出來放到一個新的字段中去。就得靠我們手動創(chuàng)建 mapping model 了。例如我們現(xiàn)在想把上面 Clazz 表刪除,原來的 Student 中的 clazz 字段用 clazzName 字段來代替。那么這種情況下就需要手動來創(chuàng)建 mapping model 了。
在這之前我們先用舊版的數(shù)據(jù)模型插入一些示例的數(shù)據(jù),這是插入的 Student 數(shù)據(jù):


Student-data-w600


Clazz 數(shù)據(jù):


Clazz-data-w600

Course 數(shù)據(jù):


Course-data-w600

因為 Course 和 Student 是多對多的關系,所以還會有一張關聯(lián)表:


SCoursesStudents-data-w600

這是插入示例數(shù)據(jù)的代碼:

  • - (void)insertManyStudents {
  • NSSet *science = [self scienceCourses];
  • NSSet *art = [self artCourses];
  • Clazz *clazz1 = [[Clazz alloc] initWithContext:self.persistentContainer.viewContext];
  • clazz1.clazzName = @"文科一班";
  • clazz1.classId = 1;
  • Clazz *clazz2 = [[Clazz alloc] initWithContext:self.persistentContainer.viewContext];
  • clazz2.clazzName = @"理科一班";
  • clazz2.classId = 2;
  • for (NSUInteger i = 0; i < 300; i++) {
  • NSString *name = [NSString stringWithFormat:@"student-%u", arc4random_uniform(100000)];
  • int16_t age = (int16_t)arc4random_uniform(10) + 10;
  • int16_t stuId = (int16_t)arc4random_uniform(INT16_MAX);
  • Student *student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.persistentContainer.viewContext];
  • student.name = name;
  • student.age = age;
  • student.id = stuId;
  • if (i % 2 == 0) {
  • student.clazz = clazz1;
  • student.courses = art;
  • } else {
  • student.clazz = clazz2;
  • student.courses = science;
  • }
  • }
  • NSError *error;
  • [self.persistentContainer.viewContext save:&error];
  • }
  • - (NSSet<Course *> *)scienceCourses {
  • Course *physics = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  • physics.courseName = @"物理";
  • physics.courseId = 1;
  • physics.courseChapterCount = 5;
  • Course *chemistry = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  • chemistry.courseName = @"化學";
  • chemistry.courseId = 2;
  • chemistry.courseChapterCount = 9;
  • Course *biology = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  • biology.courseName = @"生物";
  • biology.courseId = 3;
  • biology.courseChapterCount = 10;
  • NSSet *courses = [NSSet setWithObjects:physics, chemistry, biology, nil];
  • return courses;
  • }
  • - (NSSet<Course *> *)artCourses {
  • Course *chinese = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  • chinese.courseName = @"語文";
  • chinese.courseId = 4;
  • chinese.courseChapterCount = 12;
  • Course *history = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  • history.courseName = @"歷史";
  • history.courseId = 5;
  • history.courseChapterCount = 19;
  • Course *geography = [[Course alloc] initWithContext:self.persistentContainer.viewContext];
  • geography.courseName = @"地理";
  • geography.courseId = 6;
  • geography.courseChapterCount = 21;
  • return [NSSet setWithObjects:chinese, geography, history, nil];
  • }
  • 然后我們再來看一下 新創(chuàng)建的 v3 版本的數(shù)據(jù)模型的結構:
    Student 表


    Student-table-w600


    Course 表


    Course-table-w600

    這一次我們不再創(chuàng)建 Clazz 表了,因為它要被 Student 表里的 clazzName 字段代替。

    接下來創(chuàng)建 Mapping Model 文件


    Mapping-Model-w600

    創(chuàng)建過程中需要選擇 Source data model 和 Destination data model,也就是遷移的舊版和新版數(shù)據(jù)模型版本,分別選擇 v2 和 v3 版本:


    Source-data-model-w600
    Target-data-model-w600

    最后保存的文件名建議按一定的規(guī)則來命名,后期也方便查找:


    Save-mapping-model-w600

    然后我們來認識一下 mapping model 的用法,創(chuàng)建好后,mapping model 還是會自動推斷出大多數(shù)的字段映射,例如 Student 表中除新添加的 clazzName 字段外,其他的都可以正確的推斷出來;


    StudentToStudent-mapping-w600


    當然,如果字段名修改過的話,同樣是不能推斷出來的,如 Course 表的字段:


    CourseToCourse-mapping-w600


    另外每個 Entity Mapping 的名字的命名規(guī)則是以 SourceEntityNameToDestinationEntityName 來命名的,這個可以在右側的面板中修改:


    Entity-Mapping-name-w600

    下面來介紹 mapping model 中會用到的幾個對象:

    • $source - 對應著 NSMigrationSourceObjectKey,可以理解為 Source Model 的一個實體對象
    • $manager - 對應著 NSMigrationManagerKey,它代表的是 NSMigrationManager 對象,正是這個對象在遷移過程中發(fā)揮著作用,它管理著源對象和目標對象之間的關聯(lián)

    除了這兩個,還有幾個不常用的:

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

    在 mapping model 中可以通過 \$ 加對應的名字,直接訪問這幾個對象。例如上面圖中 \$source.name 就代表源對象的 name 屬性。同樣的我們就可以把其他未推斷出來的填上:


    -w600
    -w600

    然后再來看 Relationship Mapping 映射:


    對于這種關聯(lián)到外部表的字段,相對于普通字段會復雜一些,我們需要通過右側的面板來進行配置,Name 代表 RelationShip 的字段名;Key Path 代表這個字段對應的源對象上的字段,對于 courses 來說就是 $source.courses;然后是 Mapping Name,它代表這個 RelationShip 所關聯(lián)的外部表的 Entity Mapping,對于 courses 來說就是 Course 的 Entity Mapping 也就是 CourseToCourse。配置好這些后,Xcode 會生成一段長長的 Value Expression 表達式:

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

    意思就是調用 $manager 對象的 destinationInstancesForEntityMappingNamed:sourceInstances: 方法 CourseToCourse 和 \$source.courses 分別是兩個傳入?yún)?shù)。 它會根據(jù) CourseToCourse 的映射規(guī)則生成$source.courses 的目標對象。
    同樣的,我們可以據(jù)此來配置 Course 里的 students 關系:


    -w600

    所有字段都配置完后,就可以把 模型版本切換都 v3 然后運行程序。程序在運行時發(fā)現(xiàn)當前的 v3 版本數(shù)據(jù)模型和本地存儲的 v2 數(shù)據(jù)庫版本不一致,就會自動從 bundle 里尋找對應 v2 到 v3 的 Mapping Model,依據(jù)自定義的 Mapping Model,數(shù)據(jù)就會自動遷移完成。
    下面來看一下遷移完成的 v3 版本數(shù)據(jù)庫。
    Student 表:


    Student-table-v3-w600

    Course 表:


    Course-table-v3-w600

    自動生成的 Course 和 Student 之間的關聯(lián)表:


    Students-table-v3-w600

    可以看到 Student 表中的 clazz 字段已經被 clazzName 替換了。同時其他的數(shù)據(jù)也都沒有丟失。

    轉載于:https://www.cnblogs.com/Free-Thinker/p/9841965.html

    總結

    以上是生活随笔為你收集整理的coredata 数据库升级的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 欧美亚洲综合另类 | 亚洲午夜久久 | 国产女优在线播放 | 加勒比不卡视频 | 久久婷婷色综合 | 国产毛片毛片毛片 | 三级网站免费观看 | porn麻豆 | 精品久久久影院 | 捆绑裸体绳奴bdsm亚洲 | 日韩不卡视频一区二区 | 欧美日韩美女 | 国产精品video| 国产日韩精品一区二区 | 国产精品偷伦视频免费看 | 不卡影院一区二区 | 亚洲免费在线 | 天天摸夜夜操 | 偷拍青青草 | 国内外免费激情视频 | 在线中文视频 | 三a大片 | 在线无码va中文字幕无码 | 蜜臀人妻四季av一区二区不卡 | 国产一区999 | 国模视频一区二区 | 五月天视频 | 成人在线观看av | 久久精品无码Av中文字幕 | 国产精品爽爽久久久久久 | 日韩第二页 | 日本在线加勒比 | 欧美性爱精品在线 | 成人日批视频 | 亚洲性猛交 | 国精产品99永久一区一区 | 18在线观看视频 | 综合xx网 | 国产成人无码精品久久二区三 | 欧美日韩视频无码一区二区三 | 欧美色图1 | aaa一级黄色片 | 麻豆视频免费看 | 亚洲 欧美 另类 综合 偷拍 | 武林美妇肉伦娇喘呻吟 | 欧美熟妇精品黑人巨大一二三区 | 日本久久久久久 | 久久久久久久国产精品美女 | 黄色av免费网站 | 美国av大片 | 亚洲精品一区二区三区在线 | 国产一在线 | 国产情侣小视频 | 成人h动漫精品一区二区器材 | 日韩三级视频在线 | 久久婷婷五月国产色综合激情 | 久久久久少妇 | 久久性生活视频 | 人妻久久一区二区 | 国产伦精品一区二区三区妓女下载 | h片免费网站 | 午夜试看120秒 | 亚洲麻豆一区 | 国产在线中文 | 清纯唯美第一页 | 国产一区二区三区四区在线观看 | 亚洲国产999| 久久精品第一页 | 欧美一区网站 | www色亚洲| 亚洲婷婷网 | 法国空姐电影在线 | 91香蕉视频在线观看免费 | 国产乱人伦app精品久久 | 日韩中文字幕av电影 | 精品视频一区二区三区四区五区 | 欧美三级一区二区三区 | 欧美人妻一区二区 | 性毛片| www.亚洲.com| 婷婷第四色| 精品人妻在线一区二区三区 | 成为性瘾网黄的yy对象后 | 亚洲av无码国产精品永久一区 | 黄色的网站免费看 | 亚洲一级伦理 | 黄色成年人网站 | 夜夜夜夜爽 | 99热这里只有精品久久 | 98国产视频| 一级黄毛片 | 色婷婷婷| 日本人jizz | 日本精品一区二区在线观看 | 色哟哟精品一区 | 天天射天天搞 | 久久色av| 天堂在线免费观看视频 | 亚洲人午夜射精精品日韩 |