在Core Data中将多对多关系迁移到连接表

我有一个iPhone应用程序,使用多对多的关系将标签和笔记链接在一起。 我目前正在使用核心数据的“关系”function来实现这一点,但想迁移到使用连接表。

这是我的挑战:我想从旧模型迁移到连接表模型,我需要弄清楚如何执行数据迁移。

有没有什么好的例子,如何做到这一点?

更新:我在这里澄清我的问题,以帮助这里发生了什么:我想尝试使用Simperium来支持我们的应用程序,但Simperium不支持多对多的关系(!)。

作为我想要做的一个例子,我们以iPhoneCoreDataRecipes应用程序为例。

这是我的核心数据scheme目前类似: 在这里输入图像说明

…这是我正在过渡到: 在这里输入图像说明

我如何从一个到另一个,并与我的数据?

核心数据迁移的苹果文档是非常稀疏的,我没有看到使用NSEntityMapping或NSMigrationManager子类来完成工作的任何有用的演练。

这里是基本的过程:

  1. 创build数据模型的版本化副本。 (select模型,然后编辑器 – >添加模型版本)

  2. 对数据模型的新副本进行更改

  3. 将新数据模型的副本标记为当前版本。 (单击顶级xcdatamodel项目,然后在文件检查器中将“Versioned Data Model”部分下的“Current”条目设置为您在步骤1中创build的新数据模型。

  4. 更新您的模型对象以添加RecipeIngredient实体。 也可以使用在步骤2中创build的新关系,将配方和配料实体中的配料和配方关系replace为食谱配方实体。 (两个实体都得到了这个关系,我称之为我的recipeIngredients)显然,无论你在旧的代码中创build从成分到配方的关系,你现在都需要创build一个RecipeIngredient对象..但这超出了这个答案的范围。

  5. 在模型(文件 – >新build文件…->(核心数据部分) – >映射模型)之间添加一个新的映射,这将为您自动生成几个映射RecipeToRecipe,IngredientToIngredient和RecipeIngredient。

  6. 删除RecipeIngredient映射。 同时删除RecipeToRecipe和IngredientToRecipe(或者你在第2步中调用的任何东西)的recipeIngredient关系映射。

  7. 将RecipeToRecipe映射拖到映射规则列表中最后一个。 (这一点非常重要 ,因为我们确信配方在配方前已经迁移过,所以我们可以在迁移食谱的时候将它们连接起来。)迁移将按照迁移规则列表进行。

  8. 为RecipeToRecipe映射“DDCDRecipeMigrationPolicy”设置一个自定义策略(这将覆盖食谱对象的自动迁移,并给我们一个钩子,我们可以执行映射逻辑。

  9. 创buildDDCDRecipeMigrationPolicy通过inheritanceNSEntityMigrationPolicy食谱来覆盖createDestinationInstancesForSourceInstance(请参阅下面的代码)。 这将被调用一次为每个食谱,这将让我们创build食谱对象,以及相关的RecipeIngredient对象,将它链接到成分。 我们只会让Ingredient通过Xcode在步骤5中为我们自动创build的映射规则自动迁移。

  10. 无论您在哪里创build持久对象库(可能是AppDelegate),都要确保将用户字典设置为自动迁移数据模型:

 if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, nil] error:&error]) { } 

子类NSEntityMigrationPolicy for Recipes

 #import <CoreData/CoreData.h> @interface DDCDRecipeMigrationPolicy : NSEntityMigrationPolicy @end 

* 覆盖DDCDRecipeMigrationPolicy.m中的createDestinationInstancesForSourceInstance *

 - (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError **)error { NSLog(@"createDestinationInstancesForSourceInstance : %@", sInstance.entity.name); //We have to create the recipe since we overrode this method. //It's called once for each Recipe. NSManagedObject *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@"Recipe" inManagedObjectContext:[manager destinationContext]]; [newRecipe setValue:[sInstance valueForKey:@"name"] forKey:@"name"]; [newRecipe setValue:[sInstance valueForKey:@"overview"] forKey:@"overview"]; [newRecipe setValue:[sInstance valueForKey:@"instructions"] forKey:@"instructions"]; for (NSManagedObject *oldIngredient in (NSSet *) [sInstance valueForKey:@"ingredients"]) { NSFetchRequest *fetchByIngredientName = [NSFetchRequest fetchRequestWithEntityName:@"Ingredient"]; fetchByIngredientName.predicate = [NSPredicate predicateWithFormat:@"name = %@",[oldIngredient valueForKey:@"name"]]; //Find the Ingredient in the new Datamodel. NOTE!!! This only works if this is the second entity migrated. NSArray *newIngredientArray = [[manager destinationContext] executeFetchRequest:fetchByIngredientName error:error]; if (newIngredientArray.count == 1) { //Create an intersection record. NSManagedObject *newIngredient = [newIngredientArray objectAtIndex:0]; NSManagedObject *newRecipeIngredient = [NSEntityDescription insertNewObjectForEntityForName:@"RecipeIngredient" inManagedObjectContext:[manager destinationContext]]; [newRecipeIngredient setValue:newIngredient forKey:@"ingredient"]; [newRecipeIngredient setValue:newRecipe forKey:@"recipe"]; NSLog(@"Adding migrated Ingredient : %@ to New Recipe %@", [newIngredient valueForKey:@"name"], [newRecipe valueForKey:@"name"]); } } return YES; } 

我会在Xcode和示例Xcode项目中发布设置的图片,但是我似乎没有任何有关堆栈溢出的声望点,所以它不会让我。 我也将这张贴到我的博客。 bingosabi.wordpress.com/。

另外请注意,Xcode核心数据模型映射的东西是有点片状,偶尔需要一个“干净”,良好的Xcode rester,模拟器反弹或所有上述得到它的工作。

正如我在这个问题的评论中所build议的那样,你可能不想改变你的数据模型,而是在你的模型和图书馆之间build立一个不理解多对多关系的桥梁。

您想要创build的连接表实际上已经存在,您只需要另一种方法将数据呈现给该库。

这是否可行,取决于这个库如何看待你的模型。 查询实体的属性有多种方式,或者可以是指定要复制哪些属性/关系的方法。

很难给出真正的答案,没有任何细节,但总体思路是:

你有一些头像看起来像pipe理对象:

 // Recipe.h @interface Recipe : NSManagedObject @property (nonatomic,retain) NSSet *ingredients; @end 

现在你添加到这个对象一些额外的方法,使用一个类别:

 // Recipe+fakejoin.h @interface Recipe (fakejoin) -(NSSet*)recipeIngredients; @end 

并在此方法的Recipe+fakejoin.m中实现,该方法返回带有RecipeIngredients对象的NSSet

但正如我所说,如果这个图书馆允许你在不破坏东西的情况下玩这个游戏,这是一个悬而未决的问题。 如果这一切对你来说都是新的,那么最好find另一种解决scheme