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

时间:2012-06-24 03:38:03

标签: iphone ios core-data core-data-migration simperium

我有一个iPhone应用程序,它使用多对多关系将标签和笔记链接在一起。我目前正在使用Core Data的“关系”功能来实现这一目标,但我希望转而使用连接表。

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

有没有关于如何做到这一点的好例子?

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

作为我正在尝试做的一个例子,让我们以iPhoneCoreDataRecipes应用程序为例。

以下是我的核心数据方案目前的类似内容: enter image description here

......这就是我要转换的内容: enter image description here

如何从一个到另一个,并随身携带数据?

Apple的Core Data Migration文档非常稀疏,我没有看到任何有用的演练来使用NSEntityMapping或NSMigrationManager子类来完成工作。

2 个答案:

答案 0 :(得分:25)

以下是基本流程:

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

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

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

  4. 更新模型对象以添加RecipeIngredient实体。同时使用您在步骤2中为RecipeIngredient实体创建的新关系替换配方和成分实体上的成分和配方关系。 (两个实体都添加了这种关系。我称之为recipeIngredients)显然,无论你在旧代码中创建从配料到配方的关系,你现在都需要创建一个RecipeIngredient对象..但这超出了这个答案的范围。 / p>

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

  6. 删除RecipeIngredient映射。同时删除它为RecipeToRecipe和IngredientToRecipe提供的recipeIngredient关系映射(或者您在步骤2中调用它们的任何内容)。

  7. 将RecipeToRecipe Mapping拖动到映射规则列表中的最后一个。 (这是重要,以便我们确保在食谱之前迁移成分,以便我们可以在迁移食谱时将它们链接起来。)迁移将按迁移规则列表的顺序进行。

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

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

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

  11. 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
    

    * 覆盖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,模拟器弹跳或所有上述功能。

答案 1 :(得分:1)

正如我在问题的评论中所建议的那样,您可能不想更改数据模型,而是在模型和不理解多对多关系的库之间建立桥梁。

您要创建的联接表实际上已经存在,您只需要另一种方式将数据呈现给此库。

这是否可行,取决于此库如何查看您的模型。它有多种方法可以查询实体的属性,也可能是指定要复制哪些属性/关系的方法。

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

您有一些带有标题的托管对象:

// Recipe.h

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

现在您使用类别

向该对象添加一些额外的方法
// Recipe+fakejoin.h

@interface Recipe (fakejoin)
-(NSSet*)recipeIngredients;
@end

以及此方法的Recipe+fakejoin.m中的实现,该实现返回NSSetRecipeIngredients个对象。

但正如我所说,这是一个悬而未决的问题,如果这个图书馆允许你这样玩,而不会破坏东西。如果所有这些对您来说都是新的,那么最好找到另一种解决方案......