首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NSIncrementalStore子类中的乐观锁定支持

NSIncrementalStore子类中的乐观锁定支持
EN

Stack Overflow用户
提问于 2013-04-04 21:03:37
回答 2查看 1K关注 0票数 5

我正在实现一个自定义的NSIncrementalStore子类,它使用关系数据库进行持久存储。其中一件事,我仍然挣扎是支持乐观锁定。

(请跳过这个冗长的描述,下面是我的问题)

我分析了核心数据的SQLite增量存储是如何通过检查它生成的SQL日志来解决这个问题的,并得出了以下结论:

  • 数据库中的每个实体表都有一个Z_OPT列,该列指示该实体(行)的特定实例从1(初始插入)开始被修改的次数。
  • 每次修改托管对象时,相应数据库行中的Z_OPT值都会增加。
  • 存储库维护NSIncrementalStoreNode实例的缓存(在核心数据文档中称为行缓存),每个实例都具有一个版本属性,该属性等于以前的SELECTUPDATE SQL查询对托管对象行返回的Z_OPT值。
  • 当从NSManagedObjectContext返回托管对象时(例如,通过在其上执行NSFetchRequest ),MOC将创建该对象的快照,该对象包含该版本号。
  • 当对象被修改或删除时,Core数据通过比较缓存行和对象快照的版本,确保它没有在上下文之外被修改或删除。当在对象所属的上下文中调用-save:时,所有这些都会发生。如果版本不同,则根据set合并策略检测和处理合并冲突。

在保存MOC时,将为每个修改/删除对象调用-newValuesForObjectWithID:withContext:error:方法,这些对象将返回带有版本号的NSIncrementalStoreNode。然后将此版本与快照的版本进行比较,如果它们不同,则保存失败,出现适当的合并冲突(至少使用默认的合并策略)。

这个简单的用例与我的存储区一起正常工作,因为-newValuesForObjectWithID:withContext:error:首先检查行缓存,如果对象在其他上下文中使用相同的存储实例被并发修改,这就足够了。如果是这样的话,那么缓存包含更新的行,其版本号更高,足以检测到冲突。

但是,如何检测到底层数据库在我的存储之外已经被修改了,可能是由其他应用程序或使用相同数据库文件的其他存储实例修改的呢?我知道这是一种不常见的边缘情况,但是Core数据正确地处理了它,我更愿意这样做。

核心数据的存储使用这样的SQL查询来更新/删除对象的行:

代码语言:javascript
复制
UPDATE ZFOO SET Z_OPT=Y, (...) WHERE (...) AND Z_OPT=X
DELETE FROM ZFOO WHERE (...) AND Z_OPT=X

其中:

X -商店最后知道的版本号(来自缓存)

Y新版本号

如果这样的查询失败(没有受影响的行),则将在存储的缓存中更新该行,并将其版本与以前缓存的查询进行比较。

我的问题是:自定义NSIncrementalStore如何告知核心数据某些更新/删除/锁定对象发生了乐观锁定失败?只有商店才能知道,当它处理传递给它的-executeRequest:withContext:error:方法时,NSSaveChangesRequest。

如果基础数据库在存储区下不发生更改,则会检测到冲突,因为在存储中执行保存更改请求之前,核心数据将对每个修改/删除/锁定对象调用-newValuesForObjectWithID:withContext:error:。我找不到任何方法让NSIncrementalStore告诉核心数据,在开始处理保存请求后,发生了乐观的锁定故障。有什么非法的方法吗?在这种情况下,核心数据似乎抛出了一些异常,然后神奇地将其转换为失败的保存请求,NSError列出了所有冲突。我只能通过从-executeRequest:withContext:error:返回零并自己创建错误消息来模拟这一点。我认为在这个场景中必须有一种方法来使用标准的Core数据冲突处理机制。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-04-05 11:17:30

我意识到这并不能回答您的问题,但我将尝试给出我对CoreData和数据库的相关性的看法:

(一级缓存)

NSPesistentStoreCoordinator + NSPersistentStore ==到数据库的单个连接

(二级缓存)

保存更改的连接上的NSManagedObjectContext ==缓存

因此,据我理解,您的问题是,您有多个连接到您的商店,每次进行更改,但您没有中央版本控制您的记录。您的商店将收到一个带有-executeRequest:withContext:error:NSSaveRequestType

然后,您将负责验证记录版本是否匹配,如果在连接级别(级别1)中发现冲突,则报告上下文(级别2)和协调程序之间的版本不匹配。

您需要报告连接(级别1)与存储之间的版本不匹配。

为了能够做到这一点,您的存储必须报告对它的所有连接(ConnectionManager)对它的更改,或者它可能为在其上执行的更改提供钩子。

我不是SQLite专家,但是SQLite API在这方面确实提供了一些东西:

更新钩

提交钩

变化

变动共计

(我没有设置这类钩子的经验,但是如果CoreData使用它们,它将不会显示在调试日志中)

您可以通过设置错误指针(NSError**)并将其内部数据设置为与CoreData协调器设置的数据匹配来报告这些错误(创建合并冲突并根据需要设置其中的信息)。

请注意,乐观锁定失败只会在-executeRequest:withContext:error:期间发生(除非您与存储区有恶意连接,管理器没有跟踪该连接)。

为了支持这种行为,您的经理可能需要验证每条记录,因为它是为了节省巨大的性能成本而提交的,或者在最近对记录所做的更改中使用一些挂钩)

要处理到您的商店的多个连接,您可能需要有一个共享的NSIncrementalStoreNode缓存(按商店网址键):

静态@{

url1 : actualCacheMapping1,

url2 : actualCacheMapping2,

..。

}

保存到存储区的每个连接都将再次验证存储url实际缓存。

希望这对你有意义。

票数 1
EN

Stack Overflow用户

发布于 2014-09-24 03:35:20

我的问题是:自定义NSIncrementalStore如何告知核心数据某些更新/删除/锁定对象发生了乐观锁定失败?只有商店才能知道,当它处理传递给它的NSSaveChangesRequest时,它的-executeRequest:withContext:error:方法。

NSIncrementalStore中,NSIncrementalStoreNodes表示存储快照。节点的version属性是乐观锁定基元。持久存储负责在存储级别检测乐观锁定故障,而托管对象上下文可以更高地检测它们。如果存储对象所在的系统被其他东西更改了,那么可能会在存储级别发生乐观的锁定故障,并且该系统的状态与持久化存储中的状态表示之间存在冲突。例如,如果存储与web服务通信,而web服务数据被另一个用户更改,等等。

如果在保存期间在存储实现中检测到乐观锁定失败,则存储区负责创建描述它的NSMergeConflict对象。这些信息将由NSPersistentStoreCoordinator传播。

代码语言:javascript
复制
[[NSMergeConflict alloc] initWithSource:managedObject newVersion:newVersion oldVersion:oldVersion cachedSnapshot:inMemorySnapshot persistedSnapshot:storedSnapshot];

快照字典应该包括所有建模的属性名称作为键以及它们的值。这不包括人际关系。对于某些存储,使用来自引用对象或NSIncrementalStoreNodes的值可能就足够了,只要它们只将建模的属性名称作为键(而且这些属性名称很容易从实体描述中获得)。

创建这些对象之后,使用代码NSErrorNSCocoaErrorDomain中创建NSPersistentStoreSaveConflictsError。userInfo对象应该包含键NSPersistentStoreSaveConflictsErrorKey,它应该包含NSMergeConflict对象的数组。从保存请求中返回该请求,NSPersistentStoreCoordinator将负责查找解决方案。回忆一下,您不应该为NSManagedObjectContext中的对象状态与您的存储之间的冲突生成合并冲突,只为存储中的任何内存或缓存状态之间的冲突以及数据被保存或持久化的地方(如web服务或数据库等)生成合并冲突。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15821652

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档