3

当一对多关系没有逆时,核心数据就会出现问题。对相关属性所做的更改不会持续存在。这是我们许多人都面临的问题,因为它可以通过谷歌搜索找到。

这是为了询问你们中的一些人是否找到了实现持久性的技巧/解决方法,除了明显的答案或添加反向关系。

背景

  • 即使在文档中不鼓励单向关系,它们也不是被禁止的。文档仅坚持在没有反向时产生的责任。
  • 核心数据文档中概述了不想要反向的原因:当您有大量项目链接到一个实体时,反向关系每次添加项目时都会加载一个大型 NSSet。消耗内存,可能无缘无故超出允许范围。

例子

在员工/部门典型范例中,如果您有大量员工可以属于多个部门,那么您需要从员工到部门的一对多关系。您希望反过来,因为每次将员工链接到部门时,都必须加载、更新和保存一个(非常)大的 NSSet。此外,如果从不删除部门实体,则图的完整性很容易维护。

请不要回答这是 core-data 的一个特性,反向关系是强制性的。这不是这样说明的,更像是一个错误而不是一个功能。发布错误报告并不能解决当前部署系统的问题。

编辑:加入实体解决方案 此编辑旨在为 Dan Shelly 在下面的回答建议提供更多亮点和讨论。

首先,回答您的第一个问题,我不是要尝试多对多,而是要真正的单向多对多。您链接的同一页面的此文本略低于您引用的文本:

单向关系

不一定要在两个方向上对关系进行建模。在某些情况下,不这样做可能很有用,例如,当一对多关系可能具有大量目标对象并且您很少可能遍历该关系时(您可能希望确保不会在关系目的地的大量对象)。但是,不对两个方向的关系进行建模会给您带来大量责任,以确保对象图的一致性、更改跟踪和撤消管理。

也就是说,如果没有强制核心数据自动生成和更新它的解决方案,那么您提出的添加连接实体的解决方案是一种可行的方法。

IMO,对于我的用例,加入实体甚至不需要与 Department 有关系。这种一对一是无用的,并且可以由保持相关部门信息的连接实体的属性代替,例如它的 objectID 或其他索引属性来到达它。

即:
DepartmentEmployee:
属性:Dept_ix(整数)
关系:员工(一对一,无效)

4

3 回答 3

1

首先回复您的评论:

IMO,对于我的用例,加入实体甚至不需要与 Department 有关系。这种一对一是无用的,并且可以由保持相关部门信息的连接实体的属性代替,例如它的 objectID 或其他索引属性来到达它。

这正是部门属性在联接关系中所做的事情。
如果您查看生成的 SQLite 结构,您将看到 Employee 实体和 Department 实体之间的附加映射表,其中仅包含它们的int64id。

现在,给定的示例是:

例子

在员工/部门典型范例中,如果您有大量员工可以属于多个部门,那么您需要从员工到部门的一对多关系。您不希望反过来,因为每次将员工链接到部门时,都必须加载、更新和保存一个(非常)大的 NSSet。此外,如果从不删除部门实体,则图的完整性很容易维护。可以很容易地实现一个简单的没有逆的一对多关系。
您可以将其视为关系“多”端中对象的另一个属性。

这个例子请求一个一对多的关系:
Employee-->>Department(一个Employee可能属于许多部门)
反之是:
Department-->Employee
因为我们不能实现一个多对多的关系如果没有逆,我们必须实现关系的 to-ONE 端,只是为了确保我们遵守框架的实现。

重申:
根据文档,我们知道如果没有定义反向关系,任何多对多关系都不会持续存在。
==>
因为我们喜欢在没有逆关系的情况下对关系建模,所以我们只将其建模为耦合的 to-ONE 部分(将其建模为对多将违反框架承诺的持久性)
认为它对定义文件夹中的文件(一个文件不能属于多个文件夹),或父子关系。
==>
我们必须将关系定义为:
部门-->员工(这没有多大意义,因为只能容纳一名员工的部门并不是真正的部门)

从另一个角度来看它(否定证明):
假设我们想违背框架并定义一个没有逆的多对多关系。
==>
这意味着我们只会在一个方向上实现它,留下一个 ... 对多关系或 ... MANY-to 关系
==>
这是同一件事不是吗(一对多关系from and entity1 to entity2)
==>
现在,如果我们有一个一对多的关系并且我们选择不实现它的逆,我们可以选择实现对多的部分吗?不,我们不能,这看起来只是多对多关系的一半==>
我们必须实现它的一对多部分。

为了更有意义,我将显示更合乎逻辑的:
Department-->>Employee 所以我们对这种一对多关系的实现将是:
Department<--Employee

这将产生以下 SQLite DB 结构:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
ZNAME

ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZDEPARTMENT (int)
ZNAME

我们现在可以在 Department 上定义一个 fetched 属性来获取属于它的所有员工:
employees谓词:department == $FETCH_SOURCE

您可以在 Department 的方法中强制执行此关系prepareForDeletion(未测试):(
您将首先userInfo在 Department 上设置字典以保存强制执行的类型)
(我将“拒绝”规则的实现留给读者:D)

- (void) prepareForDeletion
{
    [super prepareForDeletion];
    NSEntityDescription* entity = [self entity];
    NSDictionary* dict = [entity userInfo] ;
    if ([dict count]) {
        [dict enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL *stop) {
            NSArray* arr = [self valueForKey:key];
            if( [value isEqualToString:@"cascade"]) {
                for (NSManagedObject* obj in arr) {
                    [[self managedObjectContext] deleteObject:obj];
                }
            } else if ( [value isEqualToString:@"nullify"] ) {
                NSArray* arr = [self valueForKey:key];
                for (NSManagedObject* obj in arr) {
                    [obj setValue:nil forKey:@"department"];
                }

            }
        }];
    }
}

正如我所看到的,这就是你对逆向关系所能做的一切。如果您仍然认为您需要多对多关系,请参考我的另一个答案。

问候,
丹。

于 2013-04-07T18:30:18.783 回答
1


这是一个很好的问题。

但首先要做的是:它在文档
中明确说明:

“重要:你必须在两个方向上定义多对多关系——也就是说,你必须指定两个关系,每一个都是另一个的逆。你不能只在一个方向上定义一个多对多关系并试图将其用作多对多。如果这样做,最终会遇到参照完整性问题。

尽管如此,让我们描述一下问题(生成的数据库)
在定义一对多关系时,生成的数据库不会添加额外的表来映射关系。
它只在多对多关系一端的实体上设置一个属性,该属性等于引用它的最后一项。

例子:

模型:
实体:部门
关系:无
属性:名称(字符串)

实体:员工
关系:部门(对多,无操作)
属性:名称

结果数据库:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
Z2DEPARTMENTS (int)
ZNAME

ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZNAME

这种结构显然会导致数据不一致。

解决方案是持有一个实体:DepartmentEmployee在两个方向上建模一对多关系,但其中一个是单向的(Department -> DepartmentEmployee):

DepartmentEmployee:
关系:部门(对一,无操作),员工(对一,无效)

并且您必须在删除部门对象时维护该表。

希望这有点道理:)

于 2013-04-06T14:51:43.570 回答
0

您是否考虑过完全取消这种关系并以编程方式管理员工的外键?

如果您有一个从现有部门列表(选择列表等)设置属性的 UI,您可以简单地从该列表中获取主键并将其分配为departmentID您的 Employee 的属性。

然后,您应该能够validateDepartmentID:error在您的 Employee 对象上实现一个方法,该方法检查给定departmentID是否有效(即在获取的部门列表中)和/或不为空,以便您维护 Employee 和 Department 之间的引用完整性。

在获取部门中的员工列表时,您可以使用获取的属性或向部门添加实例方法,该方法返回NSFetchedResultsController包含部门员工列表的实例。

您唯一需要做的另一件事是在您的 Department 类(可能在 上-prepareForDeletion)中注入一些删除逻辑,以更新departmentID任何受影响的子记录。那取决于您的业务逻辑。

Apple docs on property validation cover如果你-prepareForDeletion-validateValue:forKey:error熟悉它们。

于 2013-04-07T22:54:11.817 回答