将多对多关系迁移到Core Data中的连接表 [英] Migrating a many-to-many relationship to a join table in Core Data

查看:176
本文介绍了将多对多关系迁移到Core Data中的连接表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个iPhone应用程序使用多对多关系将标签和注释链接在一起。我目前正在使用Core Data的关系功能来完成此操作,但是想要使用连接表进行迁移。



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



有没有什么好的例子,如何做这是?



更新:我在此澄清我的问题,以帮助解决这里的情况:我想尝试使用 Simperium 来支持我们的应用程序,但Simperium不支持多对多关系(!)。



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



这里是我的核心数据方案目前类似:



...这里是我要转换的:



如何从一个到另一个,并将数据带给我? >



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

解决方案

以下是基本过程:


  1. 创建数据模型的版本化副本。 (选择模型,然后选择编辑器→添加模型版本)


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


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


  4. 更新您的模型对象以添加RecipeIngredient实体还可以使用您在步骤2中创建的新关系将Recipe和Ingredient实体的成分和配方关系替换为RecipeIngredient Entity(两个实体都添加了这个关系,显然,无论你在旧代码中如何创建从成分到配方的关系,现在都需要创建一个RecipeIngredient对象,但这超出了这个答案的范围。


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


  6. 删除RecipeIngredient Mapping,同时删除RecipeToRecipe和IngredientToRecipe(或者你在步骤2中调用的任何内容)给你的recipeIngredient关系映射。 p>


  7. 将配方到配方映射拖动到映射规则列表中的最后。 (这是重要,因此我们确保Ingredients在Recipes之前迁移,以便我们可以在迁移配方时链接它们。)迁移将按照迁移规则列表。


  8. 为RecipeToRecipe映射DDCDRecipeMigrationPolicy设置自定义策略(这将覆盖Recipes对象的自动迁移,并给我们一个钩子,


  9. 通过将NSEntityMigrationPolicy子类化为Recipes来覆盖createDestinationInstancesForSourceInstance(参见下面的代码)创建DDCDRecipeMigrationPolicy这将被每个Recipe调用一次,创建Recipe对象,以及相关的RecipeIngredient对象,它将把它链接到Ingredient。我们只是让Xml在步骤5中为我们自动创建的映射规则让Ingredient自动迁移。


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




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



子类NSEntityMigrationPolicy用于食谱

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

* 覆盖createDestinationInstancesForSourceInstance in DDCDRecipeMigrationPolicy.m *

   - (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *) sInstance entityMapping:(NSEntityMapping *)映射管理器:(NSMigrationManager *)管理器错误:(NSError **)错误
{

NSLog(@createDestinationInstancesForSourceInstance:%@,sInstance.entity。名称);

//我们必须创建配方,因为我们覆盖了这个方法。
//每个配方调用一次。
NSManagedObject * newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@RecipeinManagedObjectContext:[manager destinationContext]];
[newRecipe setValue:[sInstance valueForKey:@name] forKey:@name];
[newRecipe setValue:[sInstance valueForKey:@overview] forKey:@overview];
[newRecipe setValue:[sInstance valueForKey:@instructions] forKey:@instructions];

(NSManagedObject * oldIngredient in(NSSet *)[sInstance valueForKey:@ingredients])
{
NSFetchRequest * fetchByIngredientName = [NSFetchRequest fetchRequestWithEntityName:@Ingredient] ;
fetchByIngredientName.predicate = [NSPredicate predicateWithFormat:@name =%@,[oldIngredient valueForKey:@name]];

//在新的数据模型中查找成分。注意!!!这只有在这是第二个实体迁移时才有效。
NSArray * newIngredientArray = [[manager destinationContext] executeFetchRequest:fetchByIngredientName error:error];

if(newIngredientArray.count == 1)
{
//创建交叉记录。
NSManagedObject * newIngredient = [newIngredientArray objectAtIndex:0];
NSManagedObject * newRecipeIngredient = [NSEntityDescription insertNewObjectForEntityForName:@RecipeIngredientinManagedObjectContext:[manager destinationContext]];
[newRecipeIngredient setValue:newIngredient forKey:@ingredient];
[newRecipeIngredient setValue:newRecipe forKey:@recipe];
NSLog(@添加迁移的成分:%@到新配方%@,[newIngredient valueForKey:@name],[newRecipe valueForKey:@name]);
}


}

return YES;
}



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



还要注意,Xcode Core Data模型映射的东西是有点片状,偶尔需要一个干净,良好的Xcode rester,模拟器反弹或上述所有加工。


I've got an iPhone app that uses many-to-many relationships to link tags and notes together. I'm currently using Core Data's "Relationships" feature to accomplish this, but would like to migrate to using a join table instead.

Here's my challenge: I'd like to migrate from the old model to the join-table model, and I need to figure out how to perform that data migration.

Are there any good examples of how to do this?

Update: I'm clarifying my question here to help out with what's going on here: I want to try using Simperium to support our app, but Simperium doesn't support many-to-many relationships (!).

As an example of what I'm trying to do, let's use the iPhoneCoreDataRecipes app as an example.

Here's what my Core Data scheme currently resembles:

...and here's what I'm transitioning to:

How do I get from one to the other, and bring the data with me?

Apple's documentation for Core Data Migration is notoriously sparse, and I don't see any useful walkthroughs for using an NSEntityMapping or NSMigrationManager subclass to get the job done.

解决方案

Here is the basic process:

  1. Create a versioned copy of the Data Model. (Select the Model, then Editor->Add Model Version)

  2. Make your changes to the new copy of the data model

  3. Mark the copy of the new data model as the current version. (Click the top level xcdatamodel item, then in the file inspector set the "Current" entry under "Versioned Data Model" section to the new data model you created in step 1.

  4. Update your model objects to add the RecipeIngredient entity. Also replace the ingredients and recipes relationships on Recipe and Ingredient entities with new relationships you created in step 2 to the RecipeIngredient Entity. (Both entities get this relation added. I called mine recipeIngredients) Obviously wherever you create the relation from ingredient to recipe in the old code, you'll now need to create a RecipeIngredient object.. but that's beyond the scope of this answer.

  5. Add a new Mapping between the models (File->New File...->(Core Data section)->Mapping Model. This will auto-generate several mappings for you. RecipeToRecipe, IngredientToIngredient and RecipeIngredient.

  6. Delete the RecipeIngredient Mapping. Also delete the recipeIngredient relation mappings it gives you for RecipeToRecipe and IngredientToRecipe (or whatever you called them in step 2).

  7. Drag the RecipeToRecipe Mapping to be last in the list of Mapping Rules. (This is important so that we're sure the Ingredients are migrated before the Recipes so that we can link them up when we're migrating recipes.) The migration will go in order of the migration rule list.

  8. Set a Custom Policy for the RecipeToRecipe mapping "DDCDRecipeMigrationPolicy" (This will override the automatic migration of the Recipes objects and give us a hook where we can perform the mapping logic.

  9. Create DDCDRecipeMigrationPolicy by subclassing NSEntityMigrationPolicy for Recipes to override createDestinationInstancesForSourceInstance (See Code Below). This will be called once for Each Recipe, which will let us create the Recipe object, and also the related RecipeIngredient objects which will link it to Ingredient. We'll just let Ingredient be auto migrated by the mapping rule that Xcode auto create for us in step 5.

  10. Wherever you create your persistent object store (probably AppDelegate), ensure you set the user dictionary to auto-migrate the data model:

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

Subclass NSEntityMigrationPolicy for Recipes

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

*Override createDestinationInstancesForSourceInstance in DDCDRecipeMigrationPolicy.m *

- (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;
}

I'd post a picture of the setup in Xcode and the sample Xcode project, but I don't seem to have any reputation points on stack overflow yet... so it won't let me. I'll post this to my blog as well. bingosabi.wordpress.com/.

Also note that the Xcode Core Data model mapping stuff is a bit flaky and occasionally needs a "clean", good Xcode rester, simulator bounce or all of the above get it working.

这篇关于将多对多关系迁移到Core Data中的连接表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆