NHibernate 和字符串主键

Posted

技术标签:

【中文标题】NHibernate 和字符串主键【英文标题】:NHibernate and string primary keys 【发布时间】:2009-02-26 01:50:52 【问题描述】:

我们有一个使用字符串作为主键的旧数据库。我想在该遗留数据库之上实现对象,以更好地实现一些业务逻辑并为用户提供更多功能。

我在某些地方读到,在表上使用字符串作为主键是不好的。我想知道这是为什么?是因为区分大小写的问题吗?字符集?

...为什么这对 NHibernate 尤其不利?

...并跟进...如果字符串确实构成了错误的主键,是否值得用整数或 GUID 等替换数据库中的主键? (我们只涉及大约 25-30 张桌子)

【问题讨论】:

【参考方案1】:

好的,我会尝试一下。我将给出几个简短的警告 - 我不是数据库专家,我的经验是使用 Hibernate (Java) 而不是 NHibernate,但这里有。

我认为主键作为字符串的问题与用于在数据库中表示它们的 SQL 数据类型有关。由于在插入、查询等操作时始终使用主键,因此数据库引擎不得不花费大量时间来比较主键。如果您使用的是数字,它们只是存储为计算机非常擅长快速处理的字节。一旦你开始使用字符串,这些操作的成本(主要是比较)就会显着增加。即使数据库引擎使用非常简洁的策略来比较键,将字节作为字节而不是字符串进行比较总是会更快。

不过,在现代硬件上,这个问题已经比过去少了很多,而且有了索引,这个问题几乎消失了。

我不确定为什么这在 H​​ibernate(和 NHibernate)中真的很糟糕,但根据我的经验,因为我的应用程序有一个复杂的对象图,这些对象通常引用其他持久对象,通常是列表或集合,引用都使用另一个对象的 ID 存储,并且由于我为级联保存、获取等制定的规则,这意味着主键一直在使用。 Hibernate——我非常喜欢——倾向于完全按照它的指示去做,有时人们(尤其是我!)会告诉它做一些非常愚蠢的事情。因此,即使是看似简单的更新或查询最终也会生成相当复杂的 SQL。

所以——总而言之——字符串作为主键是不好的,因为对它们进行简单操作的成本很高,而使用 Hibernate 可能会放大这一点。但在实践中,现代数据库引擎有很多巧妙的策略来确保性能影响不会那么糟糕。 (Postgres - 可能还有其他 - 默认为主键创建索引)

为了您的后续行动 - 您应该更换钥匙吗?好吧,这取决于您的应用程序的性能。如果性能至关重要,那么对于大容量和非常密集的应用程序来说,这可能是一个好主意,否则可能会带来最小的好处,缺点是必须花时间更改所有表。您可以期望得到更好的结果来完善您与 NHibernate 一起使用的策略(即获取策略以及何时进行级联保存等)。

【讨论】:

【参考方案2】:

Andy K 似乎暗示字符串不存储为字节。那会很有趣!实际上,这完全取决于字符串 PK 的长度以及您使用的排序规则。它甚至可能比 bigint 或 int identity 更快,并且几乎肯定会比 Guids 更快。如果这些字符串是您无论如何都必须搜索的东西,那么无论如何您都需要一个索引(甚至可能是聚集索引),所以为什么不将它们设为 PK!

【讨论】:

【参考方案3】:

使用字符串或字符会为您的系统添加大量accidental complexity。考虑以下问题:

如何处理区分大小写; 如何处理填充。 NHibernate 允许您插入一个较短的字符串,并且数据库会默默地向它添加填充,但它不会反映在您的持久实体中。尝试使用内存中的 ID 再次获取实体返回 null; 如何处理编码问题。 C# 使用 unicode 字符串,您的数据库可能不会。你能告诉转换将如何处理吗?我不这么认为。 合成整数键可以由大多数数据库自动生成,无需额外的努力。使用字符串,您很可能“手动”创建它们。除非您将它们隐藏在工厂后面(在 DDD 意义上),否则生成的代码会使您的域模型混乱。

虽然 andy K 提到的性能开销可以因为索引而减少,但您仍然多次在内存中进行 ID 比较(哈希映射?)并且数据库优化不适用于那里。

我一直在使用具有字符串主键且根本没有外键的旧数据库进行项目。我们不允许修改旧模式,因为旧版应用程序依赖于它的每个小方面。我觉得字符串主键比缺少的外键更能损害一致性,因为 NHibernate 处理后者非常优雅。

【讨论】:

以上是关于NHibernate 和字符串主键的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 nhibernate 更新主键

防止 NHibernate 将 Returning 子句添加到生成的插入语句中

使用 Fluent NHibernate 和 AsList() 时指定主键

Nhibernate中的多个主键?

Fluent NHibernate 主键约束命名约定

NHibernate的主键是1-1映射吗?