【问题标题】:how to properly save into coredata one to many relationship如何正确保存到coredata中的一对多关系
【发布时间】:2013-04-28 21:35:49
【问题描述】:

对于保存到 coreData 和使用 iOS 开发,我还是很陌生。

我想要达到的目标:

我希望能够在我的数据库中拥有一个具有唯一标识符 / 的用户被 idFB 拉取,并且该用户可以创建和检索他们的锻炼例程。

我走了多远?

我设法(我认为)创建了一种方法,该方法可以从与正确的User 关联的Routine entity 中正确检索routineName。请参阅fetch 方法。

我的问题:

我认为我没有使用正确的实体关系关联User (usersExercise) <--->> Routine (userID) 进行保存。换句话说,我认为我的 save 方法不正确......因为我将整个用户保存到 userID 并且感觉不对?主要是因为当它吐出Routine.userID 时,它会拉出整个关联用户而不是特定的ID?我真的不知道会发生什么

谁能帮我正确构建这些方法?我对 coreData 保存和建立正确关系的整个过程感到非常困惑。

- (void) save {
    Routine *newRoutine = [NSEntityDescription insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:context];

    newRoutine.users = [self getCurrentUser];
    newRoutine.routineName = @"myRoutine Test Name";
    NSError* error;
    [context save:&error ];

    NSLog(@"Saved now try to fetch");
    [self fetch];

}
-(void) fetch {
    NSFetchRequest *fetchRequestItems = [[NSFetchRequest alloc] init];
    NSEntityDescription *entityItem = [NSEntityDescription entityForName:@"Routine" inManagedObjectContext:context];
    [fetchRequestItems setEntity:entityItem];
    User* user = [self getCurrentUser];
    // if i try [[self getCurrentUser] usersRoutine] it shows an error

    [fetchRequestItems setPredicate:[NSPredicate predicateWithFormat:@"users == %@",user]];

    //Sort by last edit ordered
    NSArray *sortDescriptors = [NSArray arrayWithObjects:nil];
    [fetchRequestItems setSortDescriptors:sortDescriptors];
    NSError *error = nil;
    NSArray* Routines = [context executeFetchRequest:fetchRequestItems error:&error];
    NSLog(@"result %@", [(Routine *)Routines[0] users]  );


}
-(User *)getCurrentUser {
    NSEntityDescription *entityDesc = [NSEntityDescription entityForName:@"User" inManagedObjectContext:context];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDesc];

    if (_appDelegate.isFB)
    { 
        request.predicate = [NSPredicate predicateWithFormat:@"idFB LIKE %@",_appDelegate.fdID];
        NSError *error = nil;
        NSArray *matches = [[context executeFetchRequest:request error:&error] mutableCopy];

        return (User *)matches[0];
    } else
    {
        NSLog(@"CreateRoutinePOPUP NON FB TO BE TESTED");
        request.predicate = [NSPredicate predicateWithFormat:@"email LIKE %@",_appDelegate.currentUser];
        NSError *error = nil;
        NSArray *matches = [[context executeFetchRequest:request error:&error] mutableCopy];

        return (User *)matches[0];
    }

这是 fetch 中的 NSLog 正在打印的内容:

2013-04-28 22:33:26.555 iGym[7916:c07] result <User: 0xa480580> (entity: User; id: 0xa495a00 <x-coredata://D87CEBB4-016C-4A1B-802C-2D1117BB3E51/User/p1> ; data: {
    dob = "1986-12-26 00:00:00 +0000";
    email = ".com";
    firstTime = nil;
    gender = male;
    height = nil;
    idFB =3333;
    idUserExternal = 0;
    idUserInternal = 0;
    isPT = nil;
    language = "en_US";
    location = "London, United Kingdom";
    metricSystem = nil;
    name = Joan;
    nickname = nil;
    password = nil;
    surname = Thurft;
    usersExercise = "<relationship fault: 0xa4824a0 'usersExercise'>";
    usersRoutine =     (
        "0xa495f00 <x-coredata://D87CEBB4-016C-4A1B-802C-2D1117BB3E51/Routine/p6>",
        "0xa4877e0 <x-coredata://D87CEBB4-016C-4A1B-802C-2D1117BB3E51/Routine/p1>",
        "0xa4877f0 <x-coredata://D87CEBB4-016C-4A1B-802C-2D1117BB3E51/Routine/p2>",
        "0xa487800 <x-coredata://D87CEBB4-016C-4A1B-802C-2D1117BB3E51/Routine/p3>",
        "0xa487810 <x-coredata://D87CEBB4-016C-4A1B-802C-2D1117BB3E51/Routine/p4>",
        "0xa487820 <x-coredata://D87CEBB4-016C-4A1B-802C-2D1117BB3E51/Routine/p5>"
    );
    weight = nil;
})

当我添加 NSLog(@"get current result %@", [(User *)matches[0] usersRoutine] );到 getCurrentUser 方法我得到整个用户的数据和关系说

usersExercise = "<relationship fault: 0xa464730 'usersExercise'>";

【问题讨论】:

  • else循环中的这一行:return (User *)matches;是否应该返回 (User *)matches[0];

标签: iphone ios objective-c core-data


【解决方案1】:

Core Data 与使用标准数据库不同,在标准数据库中,您将一些外键(如 userID)分配给另一个表,在该表中您希望与 User 对象建立关系,然后使用该外 ID 来查找关系,如 @ 987654324@。相反,您定义实际的关系并让 Core Data 在幕后处理所有事情,以设置任何连接表或外键。

而不是您如何设置它,您只需在 User 实体中拥有 exercisesroutines 的两个关系,它们映射到 ExerciseRoutine 实体,然后您d 在ExerciseRoutine 上具有反向关系,称为users,如果它是一个拥有且属于多的关系。所以现在,您需要将usersExercise 替换为exercises,将usersRoutine 替换为routines,然后将userID 替换为users,用于ExerciseRoutine 实体。

即使您实际上并不需要这种反向关系,您仍然需要它,因为 Core Data 将它用于数据完整性目的,如果您不填充它,Xcode 会给您一个警告。

当您建立这些关系时,您将调用例程或练习,例如 user.exercises,它将返回该用户的相关练习集。正如您所注意到的,Core Data 将返回他们称之为fault 的关系,该关系将被触发,并在您实际需要该关系的内容时返回数据。存在错误,因此您只返回您需要的确切信息,而不是对数据集运行不必要的查询。

要注意的另一件事是,Core Data 不会像您所做的那样引用 userID 这样的唯一 ID。相反,Core Data 中的每个对象都有一个由[objectName objectID] 找到的唯一 ID(只有在保存到数据存储后才会永久存在)。除了特殊情况,您确实不需要将唯一 ID 设置为实体的属性。

另外,你真的不需要使用那些独特的objectID,除非你在多线程应用程序中传递对象以进行后台处理,在这种情况下NSManagedObjectID 是线程安全的,你可以使用它在后台线程/托管对象上下文中再次查找对象。

我真的建议您阅读 Core Data 的良好介绍,例如 http://www.raywenderlich.com/934/core-data-on-ios-5-tutorial-getting-started

如果您习惯于正常的数据库设置/架构,第一次转换到 Core Data 可能会有点奇怪,但是一旦您习惯了它,它实际上会快很多,并且可以处理所有幕后的繁重工作为你。


来自 cmets 的更新:

您误解了 Core Data 中的关系概念。在 Core Data 中,关系不会像典型的数据库连接关系那样返回关联的 ID。相反,它会返回一个错误,当您需要该关系中的数据时会触发该错误。因此,它不会返回整个 User 对象,而是将 fault 返回到关联的 User 对象,当您执行 exercise.user.name 之类的操作时会触发和查询该对象

您的代码的工作方式与您保存时应有的完全一样,您只是错误地假设它不是。

【讨论】:

  • 感谢您的解释! :)... 我认为我已经正确设置了关系身份,因为 CD 不会引发任何错误或警告。所以如果我是对的,你是说我在 CD 图形中所做的关联是错误的?你认为什么可以解决我的问题?谢谢
  • 是的,它们是您设置典型数据库连接关系的方式,但不是您在 Core Data 上设置关系的方式。相反,您需要将usersExercise 替换为exercises,将usersRoutine 替换为routines,然后将userID 替换为users,用于ExerciseRoutine 实体。
  • 我假设它是 has-and-belongs-to-many 关系,因此请确保在 Core Data 菜单中为这些关系选择了这些选项。
  • 这是对关系名称的任意更改,对实际功能没有影响。我用我的数据模型的图片更新了我的问题,供您查看。然后,如果我的关系是正确的。我的代码做错了什么?
  • 您误解了 Core Data 中的关系概念。在 Core Data 中,关系不会像典型的数据库连接关系那样返回关联的 ID。相反,它返回一个fault,当您需要来自该关系的数据时,它会被触发。因此,它不会返回整个 User 对象,而是将 fault 返回到关联的 User 对象,当您执行 exercise.user.name 之类的操作时,该对象会被触发和查询
【解决方案2】:

您需要使用提供的方法在一对多对象中添加“多对象”。在您的情况下,它被称为addRoutineObject:

试试这个新的保存方法:

- (void) save {

    Routine *newRoutine = [NSEntityDescription insertNewObjectForEntityForName:@"Routine" inManagedObjectContext:context];
    newRoutine.routineName = @"myRoutine Test Name";

    NSEntityDescription *entityDesc = [NSEntityDescription entityForName:@"User" inManagedObjectContext:context];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDesc];


    NSArray *matches;
    NSError *error = nil;
    if (_appDelegate.isFB)
    { 
        request.predicate = [NSPredicate predicateWithFormat:@"idFB LIKE %@",_appDelegate.fdID];
        matches = [[context executeFetchRequest:request error:&error] mutableCopy];

    } else
    {
        NSLog(@"CreateRoutinePOPUP NON FB TO BE TESTED");
        request.predicate = [NSPredicate predicateWithFormat:@"email LIKE %@",_appDelegate.currentUser];
        matches = [[context executeFetchRequest:request error:&error] mutableCopy];
    }

    if (matches.count == 0)
    {
        NSLog(@"no user matched");
    }
    else
    {
        User *aUser = [matches objectAtIndex:0];
        [aUser addRoutineObject:newRoutine];
        if (![context save:&error])
        {
            NSLog(@"couldn't save: %@", [error localizedDescription]);
        }
    }
   }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-03
    • 1970-01-01
    • 2014-09-27
    相关资源
    最近更新 更多