CoreData Migration - xcmappingmodel 編 -
先に注意:これはメモ書きです。間違い等や読みにくい等があるけど、ご了承をば。
というわけで、CoreDataのDBをソフトウェアで移行する方法です。勿論APIはありまして、http://developer.apple.com/documentation/Cocoa/Conceptual/CoreDataVersioning/Introduction/Introduction.htmlを参考して下さい。
なお下記から、エンティティの事をテーブル、プロパティの事をフィールド、と呼びます。*1
今回は一番手軽な、MappingModelから。これは、旧テーブルのデータから新テーブルへ移行する際に、それぞれのフィールドのデータに整合性を保つ為に実行させる式などを書きます。
今回はAppleのサンプルコードMigrationV2のテーブルを元に説明。
下に旧新のテーブルの画像がありますので参考にして下さい。
変更されてる部分は結構あり、リストに表すと、こんな感じになります。なお、下にテーブルとリレーションの関連図がありますので参考に。
旧 | 新 |
Chef.name | Chef.firstName, Chef.lastName |
Chef.training | 削除 |
Chef.recipes | そのまま |
Ingredient.amount | そのまま |
Ingredient.name | そのまま |
Ingredient.recipes | そのまま |
Recipe.directions | そのまま |
Recipe.name | そのまま |
Recipe.cuisine | 新しいテーブルCuisineになり、対多関連になり且つ、cuisine.nameの値になった |
Recipe.ingredients | そのまま |
なし | Recipe.cuisines == Cuisine.recipesという関連になった |
では本題のxcmappingmodelの説明。これも、下にwindowの画像がありますので参考に。
「エンティティマッピング」がテーブルを変更する為のmappingリスト。「プロパティマッピング」がフィールドの値を操作する為のリストです。
エンティティマッピング
適当なマッピングを選択してみましょう。右の一般タブにはいろいろな情報が出てきます。色々出来るみたいです。
- ソース
- 旧テーブルのテーブル名。つまり移行される方のテーブル名。デスティネーションを指定している場合、タイプはAddになり、テーブルが生成されるだけみたいです。
- デスティネーション
- 新テーブルのテーブル名。つまり移行先のテーブル名。何も選択せず、ソーステーブルのみを選択した場合はタイプはRemoveとなり、データはブラックホールへ行くでしょう。おそらく…
- マッピング名
- 新旧のテーブルのマッピング名。プロパティマッピングでも使うので、わかりやすい方が良いです。
- H
- バージョンハッシュと書いてるけど、今イチどういう役割か、このサンプルコードではわかりません。つか、右で言うどれに当たるのか…
- カスタムポリシー
- 移行の際に使用するクラス名。何も入力しなければ、タイプはTransformになります。
- ソース取得
- 「デフォルト」と「カスタム」があります。どう使うのだろう…
カスタムポリシー
移行の際に使用するクラスみたいです。
「NSEntityMigrationPolicy」クラスを継承する必要があり、Appleのサンプルの場合、
「- (NSArray *)destinationInstancesForSourceInstance:entityMapping:manager:error:」
と
「- (BOOL)createRelationshipsForDestinationInstance:entityMapping:manager:error:」
の二つを実装しています。*2
プロパティマッピング
- T
- フィールドの型です。Aだと属性(Attribute)、Rだとリレーションを表します。
- 名前
- 新テーブルのフィールド名。
- 値式
- 旧テーブルから新テーブルのフィールドに値を入れる際に、実行する、ないしは旧テーブルのフィールド名を指定する。
値式
移行に関して一番重要な部分です。
このサンプルの場合、「Recipe.cuisine」が一番変更されていますので、重点的に見ます。
まず、Recipe -> Recipeを考えます。
cuisinesというリレーションフィールドが新テーブルには増えてますので、プロパティマッピングの名前には、cuisinesというのがあります。値式は「値式を自動生成」にチェックが入り、キーパスには$sourceが入ってます。おそらくですが、$sourceは旧テーブルをさすのだと思います。*3
マッピング名には、RecipeToCuisioneが指定されてます。RecipeToCuisineはRecipeテーブルからCuisineテーブルへのマッピングです。つまりは、Cuisineテーブルを指しているのです。
値をそのまま使う所は、フィールドが属性(Attribute)の場合は、「$source.[field name]」と.でつないでいます。新しくフィールドを追加し、何も値を入れない場合は、値式は空白のままです。例えば、Recipeの「rating」フィールドは新しく追加されましたが、値は何も入れないので空白のままです。
フィールドが関連(Relation)の場合は、キーパスは「$source[.Relation name]」となり、マッピング名は先程と同様に、デスティネーションがその指定先のテーブルであるマッピング名をしていします。例えばRecipeテーブルのingredientsの場合、変更はありませんので、キーパスは「$source.ingredients」、マッピング名はingredientを指している「IngredientToIngredient」マッピングを指定します。キーパスが「$source」だけの時は旧テーブルそのものを指す事になります。
次に、増やされたテーブル「Cuisine」を見ます。
RecipeToCuisineがCuisineテーブルの生成を担っています。ソースは「Recipe」となっています。
フィールド「name」は「Recipe」テーブルの「cuisine」からなので、値式は「$source.cuisine」となっています。フィールド「recipes」は「Recipe」テーブルとのリレーションを司ってます。値式は自動生成しており、キーパスは「$source」、マッピング名は「RecipeToRecipe」となっています。
次にフィールドが分離された場合を見ましょう。
旧テーブルのChefテーブルは、「name」が「firstName」「lastName」に分離されました。つまり、nameの値を処理を通して二つに分けるのです。「fistName」の値式は下のようになっています。
FUNCTION(CAST( "MigrationFunctions", "Class" ), "getFirstName:" , $source.name)
これは、MigrationFunctionsクラスのgetFirstNameメソッドを実行すると言う事です。このようにすれば、それぞれの値を処理させることができます。なお、「getFirstName」はクラスメソッドです。
以上かな…
古い方のテーブル | |
新しい方のテーブル | |
xcmappingmodel |