即使现在回答你的问题为时已晚,我也想在这里给出另一种观点(我希望更温和),关于Hibernate使用代理键的必要性(这真的是一个建议吗?)。
首先,我想明确一个事实,即代理键(人工自动生成的键)和自然键(由具有域含义的列组成)都有优点和缺点。我并不是想说一种键类型比另一种更好。我想说的是,根据您的要求,自然键可能比替代键更好的选择,反之亦然。
关于自然键的神话
- 复合键的效率低于代理键。不!这取决于所使用的数据库引擎:
- 自然键在现实生活中并不存在。很抱歉,但它们确实存在!例如,在航空工业中,对于给定的定期航班(航空公司,出发日期,航班编号,操作后缀),以下元组将始终是唯一的。更一般地说,当一组业务数据被给定的标准保证是唯一的时,那么这组数据就是一个[好的]自然键候选者。
- 自然键“污染了子表的架构”。对我来说,这与其说是一个真正的问题,不如说是一种感觉。具有 4 列主键(每列 2 个字节)可能比单列 11 个字节更有效。此外,4 列可用于直接查询子表(通过使用 where 子句中的 4 列),而无需联接到父表。
代理键的缺点
代理键是:
- 性能问题的根源:
- 它们通常使用自动递增的列来实现,这意味着:
- 每次想要获取新 Id 时,都会往返数据库(我知道这可以使用缓存或类似 [seq]hilo 的算法进行改进,但这些方法仍然有其自身的缺点)。
- 如果有一天你需要将数据从一个架构移动到另一个架构(至少在我的公司中经常发生这种情况),那么你可能会遇到Id冲突问题。是的,我知道你可以使用UUID,但这些最后需要32个十六进制数字!(如果您关心数据库大小,那么这可能是一个问题)。
- 如果您对所有代理键使用一个序列,那么 - 当然 - 您最终将在数据库中发生争用。
- 容易 出错。序列有max_value限制,因此作为开发人员,您必须注意以下事实:
- 您必须循环序列(当达到最大值时,它将返回到1,2,...)。
- 如果使用序列作为数据的排序(随时间推移),则必须处理循环的情况(Id 为 1 的列可能比 Id max 值为 1 的行新)。
- 确保您的代码(甚至是不应发生的客户端接口,因为它应该是内部Id)支持用于存储序列值的32b / 64b整数。
- 他们不保证没有重复的数据。您始终可以有 2 行,其列值完全相同,但生成的值不同。对我来说,从数据库设计的角度来看,这是代理密钥的问题。
- 维基百科中的更多内容...
为什么Hibernate更喜欢/需要代理密钥?
如 Java Persistence with Hibernate 引用中所述:
更有经验的Hibernate用户只使用saveOrUpdate();让Hibernate决定什么是新的,什么是旧的要容易得多,特别是在具有混合状态的更复杂的对象网络中。独占 saveOrUpdate() 的唯一(不是很严重的)缺点是,如果不在数据库中触发 SELECT,它有时无法猜测实例是旧的还是新的,例如,当一个类使用自然复合键映射并且没有版本或时间戳属性时。
限制的一些表现形式(我认为,这就是我们应该如何称呼它)可以在这里找到。
结论
请不要太贴切你的意见。在相关时使用自然键,在最好使用它们时使用代理键。
希望这帮助了某人!