如何强制UNIDIRECTIONAL与
核心数据存在问题,当多对一关系没有反转时。 对相关属性所做的更改不会持续。 这是我们很多人面临的问题,因为它可以通过Google搜索找到。
这是为了询问你们中的一些人是否找到了一种诀窍/解决方法来实现持久性,除了明显的答案或添加了反向关系之外。
背景 :
例
在员工/部门典型范例中,如果拥有数量庞大的员工能够属于多个部门,则需要从员工到部门的多对多关系。 你不想逆,因为每个雇员被链接到一个部门的时候,一个(非常)大的NSSet必须加载,更新和保存。 此外,如果部门实体从未被删除,图表完整性很容易维护。
请不要回复这是核心数据的一项功能,而且这种反向关系是强制性的。 这并非如此,更像是一个bug而不是一个功能。 发布错误报告并不能解决当前部署系统的问题。
编辑:加入实体解决方案此编辑旨在让丹·谢利的答案建议更多的提示和讨论。
首先,为了回复你的第一个问题,我并不是想要多对多,而是一个真正的单向多对多。 您链接的页面与您引用的页面相比稍低一点:
单向关系
在两个方向上建立关系并非严格必要。 在某些情况下,它可能不是有用的,例如,当一对多关系可能有大量的目标对象时,并且很少可能会遍历关系(您可能希望确保您不会不必要地在关系目的地处的大量对象)。 但是,如果不在两个方向上建立关系模型,都会对您承担很多责任,以确保对象图的一致性,进行更改跟踪以及撤消管理。
这表示,如果没有解决方案强制核心数据自动生成和更新,那么您提出的添加联合实体的解决方案是一种方法。
国际海事组织,并为我的使用情况下,联合实体甚至不需要与部门的关系。 这一对一是无用的,并且可以由保持相关部门信息的连接实体的属性取代,例如其对象ID或其他索引属性以达到它。
即:
DepartmentEmployee:
属性:Dept_ix(整数)
关系:员工(一对一,无效)
这是一个很好的问题。
但首先是:
它在文档中明确指出:
“重要的是:你必须在两个方向上定义多对多的关系 - 也就是说,你必须指定两个关系,每个关系都是另一个关系的反向关系。你不能只在一个方向上定义一个多对多的关系,并尝试把它当作多对多的使用,如果你这样做,你最终会遇到参照完整性问题。“
从来没有,让我们描述这个问题(产生的数据库)
定义多对多关系时,生成的数据库不会添加额外的表来映射关系。
它只在对多关系一端的实体上设置一个属性,等于引用它的最后一个项目。
例:
模型:
实体:部门
关系:无
属性:name(字符串)
实体:员工
关系:部门(多对多,无行动)
属性:名称
结果数据库:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
Z2DEPARTMENTS(int)
ZNAME
ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZNAME
这种结构显然会导致数据不一致。
解决方案将包含一个实体: DepartmentEmployee
在两个方向上建模多对多关系,但其中一个将是单向的(Department - > DepartmentEmployee):
DepartmentEmployee:
关系:部门(一对一,不采取行动),员工(一对一,无效)
并且在删除部门对象时必须维护表格。
希望这有些道理:)
首先回复你的评论:
国际海事组织,并为我的使用情况下,联合实体甚至不需要与部门的关系。 这一对一是无用的,并且可以由保持相关部门信息的连接实体的属性取代,例如其对象ID或其他索引属性以达到它。
这正是部门财产在联合关系中所做的。
如果您要查看生成的SQLite结构,您将看到Employee实体和Department实体之间的附加映射表,仅保存它们的int64
ID。
现在,给出的例子是:
例
在员工/部门典型范例中,如果拥有数量庞大的员工能够属于多个部门,则需要从员工到部门的多对多关系。 您不需要反过来,因为每次员工链接到部门时,必须加载,更新和保存(非常)大的NSSet。 此外,如果部门实体从未被删除,图表完整性很容易维护。 一个简单的一对多的关系,没有反转,可以很容易地实现。
您可以将它视为关系“多边”一侧的对象上的另一个属性。
这个例子请求了一种一对多的关系:
员工 - >>部门(员工可能属于多个部门)
反过来是:
部门 - >员工
由于我们不能实现多对多的关系而没有相反的关系,所以我们必须实现关系的一方,以确保我们遵守框架的实施。
重新迭代:
通过文档我们知道,如果没有定义反向关系,不会存在多对多关系。
==>
由于我们喜欢在没有反转的情况下对关系进行建模,因此我们只会将它建模为耦合的to-one部分(将它建模为to-many会违反框架所承诺的持久性)
把它看作在文件夹中定义文件(一个文件可能不属于多个文件夹)或父子关系是有用的。
==>
我们必须将关系定义为:
部门 - >员工(这是没有什么道理的,因为只有一个员工的部门实际上不是一个部门)
从另一个天使来看它(负面证据):
假设我们想要违背框架并且定义一个多对多的关系而不是相反的。
==>
这意味着我们只会在一个方向上实施,留下......多对多的关系或......多对多的关系
==>
这同样是不同的(从entity1到entity2的多对多关系)
==>
现在,如果我们有一对多的关系,而且我们选择不实现它的逆,我们可以选择实现多对多的部分? 不 , 我们不能 ,这只会成为多对多关系的一半==>
我们必须实施它的一部分。
为了更有意义,我将展示更合乎逻辑的:
部门 - >>员工因此,我们对此一对多关系的实施将是:
系< - 员工
这将导致以下SQLite数据库结构:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
ZNAME
ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZDEPARTMENT(int)
ZNAME
我们现在可以在Department上定义一个抓取的属性来获取属于它的所有员工:
employees
谓词: department == $FETCH_SOURCE
您可以在Department的prepareForDeletion
方法中强制执行此关系(未测试):
(您将首先在Department上设置userInfo
字典以保存强制类型)
(我给读者留下了“拒绝”规则的实施: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"];
}
}
}];
}
}
正如我所看到的,这是关于反向关系所能做的。 如果你仍然相信你需要多对多的关系,请参考我的其他答案。
问候,
担。
您是否考虑彻底废除关系并以编程方式管理员工的外键?
如果您有一个用户界面来设置现有部门列表中的属性(选择列表等),您可以简单地从该列表中获取主键,并将其指定为员工的departmentID
属性。
然后,您应该能够在Employee对象上实现validateDepartmentID:error
方法,该方法检查给定的departmentID
是否有效(即位于部门的提取列表中)和/或不为null,以便保持Employee和部。
在提取部门中的员工列表时,您可以使用提取的属性,也可以将实例方法添加到部门,该部门返回包含部门员工列表的NSFetchedResultsController
实例。
您唯一需要做的其他事情是在您的Department类中注入一些删除逻辑(可能在-prepareForDeletion
)以更新任何受影响的子记录上的departmentID
。 这取决于你的业务逻辑。
属性验证的Apple文档覆盖了-prepareForDeletion
和-validateValue:forKey:error
如果您不熟悉它们。