我有一个iPhone应用程序,它使用多对多关系将标签和笔记链接在一起。我目前正在使用Core Data的“关系”功能来实现这一目标,但我希望转而使用连接表。
这是我的挑战:我想从旧模型迁移到连接表模型,我需要弄清楚如何执行数据迁移。
有没有关于如何做到这一点的好例子?
更新:我在这里澄清我的问题,以帮助解决这里发生的事情:我想尝试使用Simperium来支持我们的应用程序,但Simperium不支持多对多关系(! )。
作为我正在尝试做的一个例子,让我们以iPhoneCoreDataRecipes应用程序为例。
以下是我的核心数据方案目前的类似内容:
......这就是我要转换的内容:
如何从一个到另一个,并随身携带数据?
Apple的Core Data Migration文档非常稀疏,我没有看到任何有用的演练来使用NSEntityMapping或NSMigrationManager子类来完成工作。
答案 0 :(得分:25)
以下是基本流程:
创建数据模型的版本化副本。 (选择模型,然后选择编辑器 - >添加模型版本)
对数据模型的新副本进行更改
将新数据模型的副本标记为当前版本。 (单击顶级xcdatamodel项,然后在文件检查器中将“Versioned Data Model”部分下的“Current”条目设置为您在步骤1中创建的新数据模型。
更新模型对象以添加RecipeIngredient实体。同时使用您在步骤2中为RecipeIngredient实体创建的新关系替换配方和成分实体上的成分和配方关系。 (两个实体都添加了这种关系。我称之为recipeIngredients)显然,无论你在旧代码中创建从配料到配方的关系,你现在都需要创建一个RecipeIngredient对象..但这超出了这个答案的范围。 / p>
在模型之间添加新的映射(文件 - >新文件...->(核心数据部分) - >映射模型。这将为您自动生成多个映射.RecipeToRecipe,IngredientToIngredient和RecipeIngredient。
删除RecipeIngredient映射。同时删除它为RecipeToRecipe和IngredientToRecipe提供的recipeIngredient关系映射(或者您在步骤2中调用它们的任何内容)。
将RecipeToRecipe Mapping拖动到映射规则列表中的最后一个。 (这是重要,以便我们确保在食谱之前迁移成分,以便我们可以在迁移食谱时将它们链接起来。)迁移将按迁移规则列表的顺序进行。
为RecipeToRecipe映射设置自定义策略“DDCDRecipeMigrationPolicy”(这将覆盖Recipes对象的自动迁移,并为我们提供一个钩子,我们可以在其中执行映射逻辑。
通过为配方子类化NSEntityMigrationPolicy创建DDCDRecipeMigrationPolicy以覆盖createDestinationInstancesForSourceInstance(请参阅下面的代码)。这将为每个食谱调用一次,这将让我们创建食谱对象,以及将它链接到成分的相关RecipeIngredient对象。我们将在步骤5中通过Xcode自动为我们创建的映射规则自动迁移Ingredient。
无论您在何处创建持久对象存储(可能是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
* 覆盖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
中的实现,该实现返回NSSet
个RecipeIngredients
个对象。
但正如我所说,这是一个悬而未决的问题,如果这个图书馆允许你这样玩,而不会破坏东西。如果所有这些对您来说都是新的,那么最好找到另一种解决方案......