托管对象数据一对多写入数据

时间:2015-10-31 19:30:54

标签: ios core-data

我的小应用程序中有一个数据模型,有两个小表:Test和subTest,其中Test可以有很多子测试。

在Test表中我们可以添加任意数量的项目,对于Test表中的每个项目,subTest表中都有一堆项目。

我可以通过使用CoreData谓词然后使用valueForKeyPath轻松地从与Test1,Test2等关联的subTest表中请求数据。但是,我有将数据保存到subTest表的问题。我无法管理,如何以某种方式写入数据,以后我可以通过传递测试名称(例如Test1)然后使用valueForKeyPath获取此数据。

这是一个澄清我的意思的例子。

在表中,Test是与一组子测试(st1,st2)相关联的Test1。现在必须将subtest st3添加到subTest表中。稍后我们想要获得与Test1相关的一系列子测试:

Test test = call here the method which returns NSManagedObject;
NSMutableSet setOfSubTests = [test valueForKeyPath testTosubtests.toTest]; /* Returns st1 st2 and st3 */

是否只是使用旧学校索引将数据写入subTest表? (Test1有索引1,在subTest表中与之关联的所有子测试都有Test1索引)?在iOS中使用CoreData是正确的吗?我可以应用与常见SQL DB相同的原则吗?

所有这些东西(CoreData和NSManagedObject)对我来说都是新手。我希望更好地理解它们。

1 个答案:

答案 0 :(得分:1)

您需要停止将核心数据视为数据库表。核心数据旨在管理相关对象的集合。它的底层实现可能是SQL数据库,平面二进制文件或某些自定义存储实现。 不要将其视为包含表格的数据库。

现在,至于你的例子......

  

我的小应用程序中有一个数据模型,有两个小表:   测试和子测试,其中测试可以有许多子测试。

您将拥有一个Test实体和一个Subtest实体。测试实体与Subtest有多对多的关系,因为一个测试可以“持有”许多Subtest实体。

Subtest实体与Test有一对一的关系,因为Subtest只能属于一个Test。

我在代码中理解的东西更好,因此在代码中描述时,模型可能看起来像这样。

- (NSManagedObjectModel*)modelForTestsAndSubtests {
    NSEntityDescription *testEntity = [[NSEntityDescription alloc] init];
    testEntity.name = @"Test";
    NSAttributeDescription *testName = [[NSAttributeDescription alloc] init];
    testName.name = @"name";
    testName.attributeType = NSStringAttributeType;

    NSEntityDescription *subtestEntity = [[NSEntityDescription alloc] init];
    subtestEntity.name = @"Subtest";
    NSAttributeDescription *subtestName = [[NSAttributeDescription alloc] init];
    subtestName.name = @"name";
    subtestName.attributeType = NSStringAttributeType;

    // A Test can have many Subtest objects in its relationship
    NSRelationshipDescription *testToSubtests = [[NSRelationshipDescription alloc] init];
    testToSubtests.optional = YES;
    testToSubtests.name = @"subtests";
    testToSubtests.destinationEntity = subtestEntity;
    testToSubtests.deleteRule = NSCascadeDeleteRule;
    testToSubtests.minCount = testToSubtests.maxCount = 0;
    testToSubtests.ordered = NO;

    // A Subtest can (and must) reference exactly one Test
    NSRelationshipDescription *subtestToTest = [[NSRelationshipDescription alloc] init];
    subtestToTest.optional = NO;
    subtestToTest.name = @"test";
    subtestToTest.destinationEntity = testEntity;
    subtestToTest.inverseRelationship = testToSubtests;
    subtestToTest.deleteRule = NSNullifyDeleteRule;
    subtestToTest.minCount = subtestToTest.maxCount = 1;

    testToSubtests.inverseRelationship = subtestToTest;

    testEntity.properties = @[testName, testToSubtests];
    subtestEntity.properties = @[subtestName, subtestToTest];

    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
    model.entities = @[testEntity, subtestEntity];
    return model;
}
  

但是,我遇到了将数据保存到subTest表中的问题。

同样,不要将其视为将数据保存到表中。可以把它想象成“我要在”Test1“中添加subtest”st1“

你会这样做......

NSManagedObject *subtest = [NSEntityDescription
    insertNewObjectForEntityForName:@"Subtest"
             inManagedObjectContext:test.managedObjectContext];
[subtest setValue:test forKey:@"test"];

请注意,当您将Subtest实体中的一对一关系分配给Test实体时,核心数据将自动设置反向关系,因此您不必将子测试添加到Test中的to-many关系实体。

现在,假设您插入10个子测试(这仅用于测试)......

for (int i = 0; i < 10; ++i) {
    NSManagedObject *subtest = [NSEntityDescription
        insertNewObjectForEntityForName:@"Subtest"
                 inManagedObjectContext:moc];
    [subtest setValue:[NSString stringWithFormat:@"st%02d", i] forKey:@"name"];
    [subtest setValue:test forKey:@"test"];
}
[moc save:&error]; // Handle failure and error appropriately...
  

我无法管理,如何以某种方式写入数据,以后我可以   通过传递测试名称(例如Test1)然后使用来获取此数据   valueForKeyPath。

所以,如果你想获得名为“Test1”的测试,你可以写这样的东西......

- (NSManagedObject*)existingTestWithName:(NSString*)name
                                   inMOC:(NSManagedObjectContext*)moc
                                   error:(NSError**)error {
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Test"];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name = %@", name];
    fetchRequest.fetchLimit = 1;

    NSManagedObject *result = nil;
    NSArray *fetched = [moc executeFetchRequest:fetchRequest error:error];
    if (fetched) {
        if (error) *error = nil;
        result = [fetched firstObject];
    }
    return result;
}

然后,您可以像这样抓住'Test1'......

NSManagedObject *test = [self existingTestWithName:@"Test1" inMOC:moc error:&error];
if (test) {
    // Do something with the Test entity that has name "Test1"
}

然后,一旦有了测试对象,就可以通过“子测试”关系访问该测试实例的所有Subtest对象。

NSSet *subtests = [test valueForKey:@"subtests"];

因此,您只需搜索集合即可找到特定的子测试...

- (NSManagedObject*)findExistingSubtestWithName:(NSString*)name
                                        forTest:(NSManagedObject*)test {
    // This is "simple" but could yield less than optimal performance
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", name];
    NSSet *subtests = [test valueForKey:@"subtests"];
    return [[subtests filteredSetUsingPredicate:predicate] anyObject];
}

但是,这会导致所有子测试都加载到内存中以执行迭代搜索。如果物体相对较小,并且有少量物体,则可以。但

或者,您实际上可以执行获取...

- (NSManagedObject*)fetchExistingSubtestWithName:(NSString*)name
                                         forTest:(NSManagedObject*)test
                                           error:(NSError**)error {
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Subtest"];
    fetchRequest.predicate = [NSPredicate
        predicateWithFormat:@"test = %@ AND name = %@", test, name];
    fetchRequest.fetchLimit = 1;

    NSManagedObject *result = nil;
    NSArray *fetched = [test.managedObjectContext executeFetchRequest:fetchRequest
                                                                error:error];
    if (fetched) {
        if (error) *error = nil;
        result = [fetched firstObject];
    }
    return result;
}

由于您似乎熟悉SQL,因此以下是核心数据为上面的fetch生成的SQL。

SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZTEST
FROM ZSUBTEST t0 JOIN ZTEST t1 ON t0.ZTEST = t1.Z_PK
WHERE ( t0.ZNAME = ? AND  t1.ZNAME = ?)  LIMIT 1

但是,这只是为了向您展示如何进行提取。您仍然应该将所有内容都视为互连对象,并且在您需要关注性能问题之前不要担心存储是SQL的实现。

注意

您可以在Xcode和第三方工具(如mogenerator)中生成子类代码,这些工具可以极大地增强您使用托管对象的体验。但是,知道这一切是如何运作的还是很好的。