【问题标题】:VoyageMongo: is it OK to override #= in persistent classes?VoyageMongo:在持久类中覆盖 #= 可以吗?
【发布时间】:2019-01-07 21:29:08
【问题描述】:

我遇到了 VoyageMongo 问题。我在编辑对象时得到了重复的对象(即更改和保存已持久化的对象),特别是那些覆盖#=#hash 的对象。

这是(简化的)案例:我有 UserAccount 类,实例变量为 emailsalt(用于密码加密)和 name。这些是#=#hash 方法:

= anObject
    (self isKindOf: anObject class)
        ifFalse: [ ^ false ].
    ^ self email = anObject email and: [ self salt = anObject salt ]

hash
    ^ (self salt hash + self email hash) hash

emailsalt 在创建时设置并且永远不会更改。现在,这是一个小脚本:

UserAccount removeAll.
20 timesRepeat: [ UserAccount new save ].
10 timesRepeat: [ UserAccount selectAll atRandom
            name: 'Joe Doe';
            save ].
UserAccount selectAll size = 20

这会生成 20 个UserAccounts(#new 在这种情况下创建一个带有随机电子邮件和盐的实例),然后随机选择 10 个并编辑它们的名称。 UserAccount selectAll 的最终大小应该保持在 20,但通常更大,这意味着它正在存储重复项。

可能的罪魁祸首:调试到VOCache,持有缓存对象的WeakKeyDictionary(在reversedObjects var中,对象本身是键)有时无法“命中”现有对象,因为随着键数组的增大,#scanFor: 开始查看不同的点(更具体地说,#startIndexFor:)。发生这种情况时,我可以看到 VOCachereversedObjects 中的对象,但 VOCache>>keyAtValue: 找不到它。

长话短说:

  • 是不是我不应该在持久对象中覆盖#=?或者...
  • 是不是我的#hash没有实现好?

或者,当然,我没有看到的任何其他问题:)

非常感谢!

PS:在 Pharo 6.1 和 7 中使用最新的 VoyageMongo 对此进行了测试。

【问题讨论】:

  • 方法keyAtValue:不使用#hash(它是键,而不是值,在Dictionary中得到散列。)所以,它不能是@ 987654349@ 方法。如果您在缓存中看到该对象,keyAtValue: 应该会找到它(除非#= 已损坏,您的情况似乎并非如此)
  • 谢谢莱安德罗。这里不直观的问题是 i.v. reversedObjects 是一个WeakKeyDictionary,其中对象是键(将编辑问题以澄清)。因此,VOCache>>keyAtValue: 调用WeakKeyDictionary>>#at:ifAbsent:,后者调用#findElementOrNil: 然后#scanFor:最后是#startIndexFor: (uff :)) 其中#hash: 用于扫描内部键数组。
  • 我明白了。在问题中包含的代码中,没有迹象表明更改name 会更改hash,因为哈希依赖于email。你能证实这一点吗?换句话说,您能否确认更改 name 后,哈希值保持不变?
  • 等一下!!如果您更改#hash 方法(您在重新实现它时所做的),您必须重新散列WeekKeyDictionary。你做到了吗?
  • 是的,实际上我每次都使用全新的缓存运行测试,尽可能地隔离案例。关于另一个问题,我只为#=#hash 使用不可变属性(即在创建时设置并且从不更改)。 WeakKeyDictionary 几乎不是问题(对于键的查找,我一定有什么不明白的地方),因为它在整个系统中被广泛使用,所以可能是 VOCache 如何使用它的问题。

标签: smalltalk pharo


【解决方案1】:

作为一般规则,您不应覆盖实体对象中的 #= 和 #hash,因为它们需要基于身份与值。

如果 2 个对象的参数值匹配,那么这并不一定意味着它们代表同一个实体;如果您确实需要覆盖#=,那么您将需要一个业务密钥。 当您从数据库中提取实体对象时,最好不要覆盖并仅使用基于身份的实体对象。即好像您正在使用 OO DB。

也许这是一个 Voyage 错误,因为 reversedObjects 变量应该是 WeakIdentityKeyDictionary?

【讨论】:

  • 谢谢!我不知道这个关于持久对象的一般规则。问题是我之前使用了一个业务密钥,并且遵守了关于#=#hash重定义的所有规则,所以我关心的是缓存,因为身份-based #hash 有效,而我的覆盖版本没有。将在潜在错误方面跟随您的领导并报告!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-19
  • 1970-01-01
  • 2013-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多