coredata 数据库升级
在真實開發中,因為需求是不斷變化的,說不定什么時候就需要往模型里添加新的字段,添加新的模型,甚至是大規模的重構;所以數據的遷移就顯得尤為重要了。
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時,需要將 options 的 NSMigratePersistentStoresAutomaticallyOption 和 NSInferMappingModelAutomaticallyOption 兩個 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
這是插入示例數據的代碼:
然后我們再來看一下 新創建的 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 表達式:
意思就是調用 $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 数据库升级的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端 html border-righ
- 下一篇: 具体knn算法概念参考knn代码pyth