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

Isa*_*aac 37 java database orm hibernate composite-key

这是来自Hibernate的官方教程:

还有一个替代<composite-id>声明允许使用复合键访问旧数据.其他任何东西都强烈建议不要使用它.

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

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

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

JB *_*zet 43

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

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

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

  • 但是在使用中你必须有这3个参数才能获得唯一的参数 - dbs也经常使用btree而不是哈希值,因此访问密钥的成本不会低于拥有单独的唯一ID是一个开销.在设计之间存在一个尚未解决的争论,即那些喜欢生成id的人和那些想要导致复合键的物理键的人. (9认同)
  • 我打算给这个+1,但是你添加了最后一个子弹点,现在我想要+10它.任何面对"级联复合键"这一可怕问题的人都会真正开始欣赏简单的代理键作为数据库设计的重要部分. (7认同)
  • 简单的多对多链接表怎么样?他们没有大多数这些问题,并且在数据库级别上制作它们的明显方法是拥有所有字段的复合主键. (4认同)
  • 如果您遇到上述任何原因,那么您需要重新考虑您的设计.另外,你已经使用复合键做出了一个好的决定.关于Hibernate的限制,请看一下mwsiri的答案. (4认同)

mwn*_*iri 38

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

首先的,我想是这样的事实,明确双方的代理键(人工自动生成的)和自然键(与域名含义列(S)组成)有优点缺点.我并不是说一个关键类型比另一个更好.我想说,根据您的要求,自然键可能是比代理更好的选择,反之亦然.

自然键上的神话

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

代理键的缺点

代理键是:

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

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

正如Java Persistence with Hibernate参考中所述:

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

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

结论

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

希望这有助于某人!

  • 我喜欢使用复合键,这是合适的,大声而自豪的!它使设计更加清洁.如果它是合适的地方,你不会面临被告知的任何缺点. (6认同)

Lui*_*ano 10

我从设计的角度考虑问题.这不仅仅是因为Hibernate认为它们好坏.真正的问题是:自然键是否适合作为我的数据的良好标识符?

在您的业务模型中,今天可以方便地通过某些数据识别记录,但业务模型会及时发展.当发生这种情况时,您会发现您的自然键不再适合唯一标识您的数据.并与其他表参照完整性,这将使事情MUCH难以改变.

拥有代理PK非常方便,因为它不会将您的数据在您的存储中您的业​​务模型结构相关联.

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

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

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

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