为什么不鼓励在休眠状态下使用复合键?关于自然键的神话代理键的缺点为什么Hibernate更喜欢/需要代理密钥?结论

2022-09-01 02:55:16

这是来自Hibernate官方教程

还有一种替代声明,允许使用复合键访问旧数据。强烈反对将其用于其他任何事情。<composite-id>

为什么不鼓励使用复合键?我正在考虑使用一个3列表,其中所有列都是外键,并共同形成一个主键,在我的模型中是一个有意义的关系。我不明白为什么这是一个坏主意,特别是我将对它们使用索引。

还有什么替代方案?创建其他自动生成的列并将其用作主键?我仍然需要查询我的3列!?

总之,为什么这种说法是真的呢?什么是更好的选择?


答案 1

他们不鼓励他们有几个原因:

  • 它们使用起来很麻烦。每次需要引用一个对象(或行)时,为了在 Web 应用程序中的示例,您需要传递 3 个参数,而不仅仅是一个参数。
  • 他们效率低下。数据库需要对 3 列的组合进行哈希处理,而不是简单地对整数进行哈希处理。
  • 它们会导致错误:开发人员不可避免地错误地实现了主键类的 equals 和 hashCode 方法。或者他们使其可变,并在存储在HashSet或HashMap中后修改其值
  • 它们污染了架构。如果另一个表需要引用此 3 列表,则需要有 3 列,而不仅仅是一列作为外键。现在假设您遵循相同的设计,并使这个 3 列外键成为这个新表的主键的一部分,你将很快得到一个 4 列的主键,然后在下一个表中有一个 5 列的 PK,依此类推,从而导致数据重复和脏架构。

另一种方法是,除了其他三列之外,还有一个自动生成的单列主键。如果要使三列的元组唯一,请使用唯一约束。


答案 2

即使现在回答你的问题为时已晚,我也想在这里给出另一种观点(我希望更温和),关于Hibernate使用代理键的必要性(这真的是一个建议吗?)。

首先,我想明确一个事实,即代理键(人工自动生成的键)和自然键(由具有域含义的列组成)都有优点缺点。我并不是想说一种键类型比另一种更好。我想说的是,根据您的要求,自然键可能比替代键更好的选择,反之亦然。

关于自然键的神话

  1. 复合键的效率低于代理键。不!这取决于所使用的数据库引擎:
  2. 自然键在现实生活中并不存在。很抱歉,但它们确实存在!例如,在航空工业中,对于给定的定期航班(航空公司,出发日期,航班编号,操作后缀),以下元组将始终是唯一的。更一般地说,当一组业务数据被给定的标准保证是唯一的时,那么这组数据就是一个[好的]自然键候选者。
  3. 自然键“污染了子表的架构”。对我来说,这与其说是一个真正的问题,不如说是一种感觉。具有 4 列主键(每列 2 个字节)可能比单列 11 个字节更有效。此外,4 列可用于直接查询子表(通过使用 where 子句中的 4 列),而无需联接到父表。

代理键的缺点

代理键是:

  1. 性能问题的根源:
    • 它们通常使用自动递增的列来实现,这意味着:
      • 每次想要获取新 Id 时,都会往返数据库(我知道这可以使用缓存或类似 [seq]hilo 的算法进行改进,但这些方法仍然有其自身的缺点)。
      • 如果有一天你需要将数据从一个架构移动到另一个架构(至少在我的公司中经常发生这种情况),那么你可能会遇到Id冲突问题。是的,我知道你可以使用UUID,但这些最后需要32个十六进制数字!(如果您关心数据库大小,那么这可能是一个问题)。
      • 如果您对所有代理键使用一个序列,那么 - 当然 - 您最终将在数据库中发生争用。
  2. 容易 出错。序列有max_value限制,因此作为开发人员,您必须注意以下事实:
    • 您必须循环序列(当达到最大值时,它将返回到1,2,...)。
    • 如果使用序列作为数据的排序(随时间推移),则必须处理循环的情况(Id 为 1 的列可能比 Id max 值为 1 的行新)。
    • 确保您的代码(甚至是不应发生的客户端接口,因为它应该是内部Id)支持用于存储序列值的32b / 64b整数。
  3. 他们不保证没有重复的数据。您始终可以有 2 行,其列值完全相同,但生成的值不同。对我来说,从数据库设计的角度来看这是代理密钥的问题。
  4. 维基百科中的更多内容...

为什么Hibernate更喜欢/需要代理密钥?

Java Persistence with Hibernate 引用中所述:

更有经验的Hibernate用户只使用saveOrUpdate();让Hibernate决定什么是新的,什么是旧的要容易得多,特别是在具有混合状态的更复杂的对象网络中。独占 saveOrUpdate() 的唯一(不是很严重的)缺点是,如果不在数据库中触发 SELECT,它有时无法猜测实例是旧的还是新的,例如,当一个类使用自然复合键映射并且没有版本或时间戳属性时。

限制的一些表现形式(我认为,这就是我们应该如何称呼它)可以在这里找到。

结论

请不要太贴切你的意见。在相关时使用自然键,在最好使用它们时使用代理键。

希望这帮助了某人!


推荐