预加载具有多对多关系的核心数据的最佳实践

时间:2014-09-21 15:45:54

标签: ios core-data many-to-many preload

我正在开发一个具有核心数据的IOS应用程序,我必须预先加载与核心数据有很多关系的数据。我有三个桌子,超市,产品和中间表名为supermarketToproduct,存储超市与产品之间的关系。正如我所说,超市和产品实体之间存在多对多的关系,因此我需要创建一个中间表来表明关系。 我的问题是,将JSON格式的数据预加载到具有多对多关系的核心数据中的最佳做法是什么。在Apples参考文档中,声明不需要创建中间表,因为coredata为我们创建了一个。但是,如何在没有中间表的情况下预加载日期时如何定义多对多关系? 顺便说一句,所有数据都是静态的,所以不需要将新数据插入表中,我只需要获取数据及其相关的超市或产品。 请注意,联接表有两个独立的属性,名为priceofProductforGivenMarket和primaryProductforGivenMarket 这是我的数据的JSON表示,也是我将在核心数据中创建的模型:

Supermarket:
name
location
phone number

Product:
name
price
company

supermarketToproduct:
 nameofsupermarket
 nameofproduct 
 priceofProductforGivenMarket
 primaryProductforGivenMarket

2 个答案:

答案 0 :(得分:1)

我认为上述方法在预取时很难,并且会预取几乎所有匹配的条目,并且预取的真正动机将会消失。

我也建议在超市表中添加topProduct。

您必须在两个方向上定义多对多关系 - 也就是说,您必须指定两个关系,每个关系都是另一个关系

编辑01:

Product (Attributes):
name
price
company

Supermarket (Attributes):
name
location
phone number
supermarket top Product

创建上述属性后,添加多对多关系。

Coredata会在下面添加关系的相应实体。

**NSSet products, [supermarket table] and NSSet supermarkets [product table]**

现在,您可以在插入数据后实际更新此关系: 即。

插入

    // Create Product
    NSManagedObject *product = [[NSManagedObject alloc] initWithEntity:@"Product" insertIntoManagedObjectContext:self.managedObjectContext];

    // Set details
    [product setValue:@"xxx" forKey:@"name"];
    ...

// Create Supermarket

    NSManagedObject *supermarket = [[NSManagedObject alloc] initWithEntity:@"SuperMarket" insertIntoManagedObjectContext:self.managedObjectContext];

    // Set First and Last Name
    [supermarket setValue:@"Main Street" forKey:@"name"];
    ...

// Create Relationship

    [supermarket setValue:[NSSet setWithObject:product] forKey:@"products"];

    // Save Managed Object Context
    NSError *error = nil;
    if (![supermarket.managedObjectContext save:&error]) {
        NSLog(@"Unable to save managed object context.");
        NSLog(@"%@, %@", error, error.localizedDescription);
    }

//And similarly inverse relationship.

<强>撷取

现在,由于您在超市表中有关于产品的相关信息,反之亦然,因此提取将是平滑的并且如前所述,不会预先预取所有数据,否则它将会完成。因为没有必要通过coreData fetch创建进一步的关系。

请按照以下了解详情: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html#//apple_ref/doc/uid/TP40001857-SW10

另请注意: Core Data sectionNameKeyPath with Relationship Attribute Performance Issue

答案 1 :(得分:1)

我会创建三个实体,Supermarket,Product和SupermaketProductDetails,其属性和关系如下:

Supermarket:
Attributes: name, location, phone number
Relationship (to many): productDetails

Product:
Attributes: name, price, company
Relationship (to many): supermarketDetails

SupermarketProductDetails:
Attributes:  priceofProductforGivenMarket, primaryProductforGivenMarket
Relationships: (to one) supermarket, (to one) product

在Xcode中,指示超市实体中productDetails关系的“目的地”是SupermarketProductDetails实体,具有逆超市,同样将SupermarketProductDetails实体设置为Product实体中的supermarketDetails的目的地,具有反向产品。 / p>

然后我会首先解析超市的JSON数据,创建超市对象并设置名称,位置和电话号码,但不输入产品关系的任何内容。同样,解析产品JSON并创建所有Products对象。然后我将解析连接表,并创建SupermarketProductDetails对象。根据您的JSON数据设置属性,并执行获取以获取具有正确名称的超市实体,同样获取具有正确名称的Product实体,然后直接设置这些关系。

修改 假设您将连接表中的每一行解析为四个NSStrings:nameofsupermarket,nameofproduct,priceofProductforGivenMarket和primaryProductforGivenMarket。然后为每一行...

// First fetch the correct Supermarket...
NSFetchRequest *supermarketFetch = [NSFetchRequest fetchRequestWithEntityName:@"Supermarket"]
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@",nameofsupermarket];
supermarketFetch.predicate = predicate;
NSError *error;
NSArray *results = [context executeFetchRequest:supermarketFetch error:&error];
// should really check for errors, and that we get one and only one supermarket
NSLog(@"Supermarket fetch returned %i results",[results count]);  // should be only 1!
mySupermarket = (NSManagedObject *)[results firstObject];
if (![[mySupermarket valueForKey:@"name"] isEqualToString:nameofsupermarket]) {
    NSLog(@"Wrong supermarket fetched");
    }

// Now fetch the product...
NSFetchRequest *productFetch = [NSFetchRequest fetchRequestWithEntityName:@"Product"]
predicate = [NSPredicate predicateWithFormat:@"name like %@",nameofproduct];
productFetch.predicate = predicate;
results = [context executeFetchRequest:productFetch error:&error];
// should really check for errors, and that we get one and only one product
NSLog(@"Product fetch returned %i results",[results count]);  // should be only 1!
myProduct = (NSManagedObject *)[results firstObject];
if (![[myProduct valueForKey:@"name"] isEqualToString:nameofproduct]) {
    NSLog(@"Wrong product fetched");
    }

// Now create the SupermarketProductDetails entity:
NSManagedObject *mySupermarketProductDetails = [NSEntityDescription insertNewObjectForEntityForName:@"SupermarketProductDetails" inManagedObjectContext:context];
// set attributes...
[mySupermarketProductDetails setValue:primaryProductForGivenMarket forKey:@"primaryProductForGivenMarket"];
[mySupermarketProductDetails setValue:priceOfProductForGivenMarket forKey:@"priceOfProductForGivenMarket"];
// set relationships...
[mySupermarketProductDetails setValue:mySupermarket forKey:@"supermarket"];
[mySupermarketProductDetails setValue:myProduct forKey:@"product"];
[context save:&error];
// should check for errors...

请注意,您只需要设置这些关系的一面--CoreData会为您更新另一面(即它会将mySupermarketDetails添加到myProduct的supermarketDetails集等)。还要注意,(一对一)关系(例如超市)的“价值”是目标对象本身(例如mySupermarket);你不要使用名称或任何其他密钥。 Coredata(隐藏在底层的sql表中)使用唯一的objectID来进行链接。

(这可能是更有效的方法,而不是为每个SupermarketProductDetails条目进行两次提取,但这将有效。)

EDIT2:请注意,上面假设您的实体都是作为NSManagedObjects实现的。如果您为每个实体创建了单独的子类,那么您可以简化上面的一些代码。例如,valueForKey:和setValue:forKey:可以使用点表示法替换为等效的属性访问器方法,例如:

[mySupermarketProductDetails setValue:primaryProductForGivenMarket forKey:@"primaryProductForGivenMarket"];

会变成:

mySupermarketProductDetails.primaryProductForGivenMarket = primaryProductForGivenMarket;

[mySupermarket valueForKey:@"name"]

可以替换为:

mySupermarket.name

同样,应该使用适当的子类而不是NSManagedObject创建对象。例如。

NSManagedObject *mySupermarketProductDetails = [NSEntityDescription insertNewObjectForEntityForName:@"SupermarketProductDetails" inManagedObjectContext:context];

会变成

SupermarketProductDetails *mySupermarketProductDetails = [NSEntityDescription insertNewObjectForEntityForName:@"SupermarketProductDetails" inManagedObjectContext:context];