首页 文章

如何强制UNIDIRECTIONAL与众多关系持续存在

提问于
浏览
3

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

这是问你是否有人找到了一个技巧/解决方法来实现持久性,除了明显的答案或添加反向关系 .

Background

  • 即使文档中不鼓励单向关系,也不禁止它们 . 该文件仅坚持在没有反向时产生的责任 .

  • 核心数据文档中概述了不希望反转的原因:当您有大量项目链接到一个实体时,每次添加项目时,反向关系都会加载一个大的NSSet . 消耗内存,可能超过允许无缘无故 .

Example

在员工/部门的典型范例中,如果您拥有大量可以属于多个部门的员工,则需要从员工到部门之间的多对多关系 . 您需要反向,因为每次员工链接到某个部门时,必须加载,更新并保存(非常)大的NSSet . 此外,如果从不删除部门实体,则图形完整性易于维护 .

请不要回答这是核心数据的一个特征,反向关系是强制性的 . 这不是这样说的,更像是一个bug而不是一个功能 . 发布错误报告并不能解决当前部署的系统的问题 .

Edit: The Join entity solution 此编辑旨在为Dan Shelly的回答提案提供更多信息和讨论 .

首先,要回复你的第一个,我不是想要多对多但是真正的单向对多 . 您链接的相同页面的文本略低于您引用的文本:

单向关系在两个方向上 Build 关系并不是绝对必要的 . 在某些情况下,例如当多对多关系可能具有非常多的目标对象并且您很少可能遍历关系时(例如,当您可能希望确保您没有不必要的错误时),它可能是有用的 . 关系目的地的大量物体) . 但是,不对两个方向的关系进行建模会给您带来很多责任,以确保对象图的一致性,变更跟踪和撤消管理 .

也就是说,如果没有解决办法迫使核心数据自动生成和更新,那么你提出的添加连接实体的解决方案是一种方法 .

IMO,对于我的用例,加入实体甚至不需要与Department有关系 . 这个to-one是无用的,可以用连接实体的属性替换,保持相关的Department信息,比如它的objectID或其他索引属性来到达它 .

即:
DepartmentEmployee:
属性:Dept_ix(整数)
关系:员工(to-one,nullify)

3 回答

  • 0

    这是一个很好的问题 .

    但首先要做的事情是:
    它明确指出documentation

    “重要:您必须在两个方向上定义多对多关系 - 也就是说,您必须指定两个关系,每个关系都是另一个关系 . 您不能只在一个方向上定义多对多关系并尝试将它用作多对多 . 如果这样做,最终会出现参照完整性问题 . “

    从来没有,Lets描述了这个问题(结果数据库)
    定义多对多关系时,生成的数据库不会添加其他表来映射关系 .
    它只在实体上设置一个属性,该属性在多对多关系的一端等于引用它的最后一项 .

    例:

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

    实体:员工
    关系:部门(对多,不采取行动)
    属性:名称

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

    ZEMPLOYEE:
    Z_PK
    Z_ENT
    Z_OPT
    ZNAME

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

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

    DepartmentEmployee:
    关系:部门(一对一,不行动),员工(一对一,无效)

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

    希望这有一定道理:)

  • 1

    首先回复你的评论:

    IMO,供我使用例如,连接实体甚至不需要与Department有关系 . 这个to-one是无用的,可以用连接实体的属性替换,保持相关的Department信息,比如它的objectID或其他索引属性来到达它 .

    这正是部门 property 在联合关系中所做的事情 .
    如果您查看生成的SQLite结构,您将看到Employee实体和Department实体之间的附加映射表,只保留它们的 int64 ID .

    现在,给出的例子是:

    示例在员工/部门典型范例中,如果您有大量员工可以属于多个部门,则需要从员工到部门之间的多对多关系 . 您不希望反向,因为每次员工链接到某个部门时,都必须加载,更新和保存(非常)大的NSSet . 此外,如果从不删除部门实体,则图形完整性易于维护 . 可以轻松实现简单的一对多关系,无逆转 . 您可以将其视为关系中“多”方面的对象上的另一个属性 .

    此示例请求类似的一对多关系:
    员工 - >>部门(员工可能属于多个部门)
    反过来是:
    部门 - >员工
    由于我们不能在没有逆的情况下实现多对多关系,因此我们必须实现关系的to-ONE方面,以确保我们遵守框架的实现 .

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

    从另一个天使(负面证明)看它:
    假设我们想要反对框架并定义一个没有逆的MANY-to-many关系 .
    ==>
    这意味着我们只会在一个方向上实现它,留下一个......多对多的关系或......很多关系
    ==>
    这是同样的事情不是它(从实体1到实体2的多对多关系)
    ==>
    现在,如果我们有一对多的关系并且我们选择不实现它的逆,我们可以选择实现to-many部分? NO WE CANNOT ,这看起来只是MANY-to-many关系的一半==>
    我们 MUST 实施它的一部分 .

    为了更有意义,我会表现出更合乎逻辑的:
    部门 - >>员工因此,我们对这种一对多关系的实施将是:
    系< - 员工

    这将导致以下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 方法中强制执行此关系(未测试):
    (您将首先在Department上设置 userInfo 字典以保持执行类型)
    (我把'Deny'规则的执行留给了读者: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"];
                    }
    
                }
            }];
        }
    }
    

    正如我所看到的,这就是你可以对反向关系做的所有事情 . 如果您仍然认为自己需要多对多关系,请参阅我的其他答案 .

    问候,
    担 .

  • 1

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

    如果您有一个UI,它从现有Departments列表(选择列表等)设置属性,您只需从该列表中获取主键并将其指定为Employee上的 departmentID 属性 .

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

    在部门中获取Employees列表时,您可以使用获取的属性或向Department返回实例的实例方法 NSFetchedResultsController 包含部门的员工名单 .

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

    Apple docs on property validation封面 -prepareForDeletion-validateValue:forKey:error 如果您不熟悉它们 .

相关问题