为什么复合键在休眠时不鼓励?

时间:2013-01-01 18:01:40

标签: java database hibernate orm composite-key

这是Hibernate official tutorial

  

还有一个替代<composite-id>声明,允许使用复合键访问旧数据。强烈建议不要使用它。

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

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

简而言之,为什么这句话是真的?什么是更好的选择?

6 个答案:

答案 0 :(得分:43)

他们劝阻他们有几个原因:

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

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

答案 1 :(得分:37)

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

首先,我想清楚两个代理键(人工自动生成的)和自然键(由列组成)具有域意义)具有专业缺点。我并不是说一个关键类型比另一个更好。我想说,根据您的要求,自然键可能是比代理键更好的选择,反之亦然。

自然键上的神话

  1. 复合键的效率低于代理键。没有!这取决于使用的数据库引擎:
  2. 自然键不存在于现实生活中。抱歉,但确实存在!例如,在航空业中,对于给定的预定航班(航空公司,departureDate,flightNumber,operationalSuffix),以下元组将始终是唯一的。更一般地说,当一组业务数据被一个给定的标准保证是唯一的时,那么这组数据就是一个[好]自然候选词。
  3. 自然键&#34;污染架构&#34;儿童表。对我来说,这更像是一种感觉而非一种真正的问题。具有2个字节的4列主键每个可能比11个字节的单个列更有效。此外,4列可用于直接查询子表(通过使用where子句中的4列)而无需连接到父表。
  4. 代理键的缺点

    代理键是:

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

      Java Persistence with Hibernate 参考:

      中所述
        

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

      限制的一些表现形式(我认为应该如何调用它)可以找到here

      结论

      请不要对你的意见过于平庸。在相关时使用自然键,并在最好使用时使用代理键。

      希望这有助于某人!

答案 2 :(得分:10)

我会从设计的角度考虑问题。这不仅仅是因为Hibernate认为它们好坏。真正的问题是:是自然键,是我数据的良好标识符的好选择吗?

在您的商业模式中,今天通过某些数据识别记录会很方便,但商业模式会随着时间的推移而发展。当发生这种情况时,您会发现您的自然键不再适合唯一标识您的数据。并且在其他表格中使用引用完整性,这将使 MUCH 更难以改变。

拥有代理PK很方便,因为它不会如何使用您的业务模型结构链接您的存储中的数据。

无法从序列中生成自然键,并且无法通过其数据识别的数据的情况更多更频繁。这证明自然密钥与存储密钥不同,并且它们不能被视为一般(和良好)方法。

使用代理键简化了应用程序和数据库的设计。它们更易于使用,性能更高,并且可以完美地完成工作。

自然键只带来缺点:我无法想到使用自然键的单一优势。

那就是说,我认为hibernate对自然(组合)键没有实际问题。但是你有时可能会发现一些问题(或错误),以及文档问题或试图获得帮助,因为hibernate社区广泛承认代理键的好处。因此,为为什么选择复合键准备一个很好的答案。

答案 3 :(得分:1)

如果正确理解了Hibernate文档:

&#34;还有一个替代<composite-id>声明,允许使用复合键访问旧数据。强烈建议不要使用它。&#34;

关于主题5.1.4的

。 id标记xml <id>,它使得主键映射过早,我们可以得出结论,hibernate文档不鼓励使用<composite-id>而不是<id> xml标记进行复合主键映射和 NOT 使任何参考否定使用复合主键。

答案 4 :(得分:0)

使用聚簇索引进行查询优化,使用数据库作为工具开发的应用程序肯定更有利于保持代理键上的工作流程。

确实需要特别注意数据仓库和OLAP样式系统,它们利用大量的Fact Table将维度的代理键绑定在一起。在这种情况下,数据指示可用于维护记录的仪表板/应用程序。

因此,不是一种方法优于另一种方法,也许一种指令对另一种方法有利,因为关键构造:您不会非常容易地开发Hibernate应用程序来利用对SSAS系统实例的直接访问。

我使用两种关键混合物开发,并且感觉要实现一个坚实的星形或雪花模式,使用聚集索引代理通常是我的首选。

所以,关于OP和其他人的问候:如果你想保持db不变的开发(Hibernate专门研究) - 利用代理方法,当数据读取速度变慢,或者你注意到某些查询会消耗性能,还原到您的特定数据库,并添加优化查询顺序的复合聚簇索引。

答案 5 :(得分:0)

不要混淆主键和唯一索引。如果您使用自然键,则将您的密钥与您的业务、业务数据相关联;它不是那么好。因此,即使可以使用一组数据来定义复合键,也不建议这样做。 在我看来,复合键主要在您拥有现有架构时可用