jpa序列ID生成

Posted

技术标签:

【中文标题】jpa序列ID生成【英文标题】:jpa sequence ID generation 【发布时间】:2014-11-21 13:44:12 【问题描述】:

在实体被持久化到数据库之前但立即在构造函数中分配一个 ID 可以获得巨大的价值:你的 equals/hashcode 实现变得微不足道,and it saves many headaches。

当实体相等基于== 时,我看到了一些问题:代理进入会话,当它作为真实对象解包时,您会得到返回falseequals()

当您覆盖 equalshashcode 以使用生成的 ID 时,因为该 ID 仅在 persist() 上生成,所有非持久实体的 ID 为 null,因此彼此相等。

根据我的阅读,当您使用传统的 ID 生成技术(比如说自动增量)时,ID 是在刷新实体管理器时生成的。当您使用基于序列的解决方案时,它是在持久化时生成的。

那篇文章和我目前的理解说,最简单的解决方案是在创建时分配一个 ID,而不是持久化或刷新时间。并且序列看起来可以访问,但 JPA 决定反对它。使用序列获取 ID 很便宜(因为您可以预取),为什么 JPA 至少没有提供在对象构建时获取基于序列的 ID 的选项?如果实体最终实际上没有持久化,则存在浪费一些 ID 的风险,但我认为这不是什么大问题。

除此之外,就解决方案的简单性和可理解性而言,唯一“不妥协”的似乎是 UUID,它们有自己的问题。

我错过了什么吗?是否有一些 JPA 身份生成器或一些基于序列并允许在构建时提供 ID 的库?

【问题讨论】:

【参考方案1】:

从写作的角度来看,使用assigned identifier 是最好的方法。它在所有entity state transitions 中也是一致的,您甚至可以在 JDBC 级别批处理多个插入。

在读取和索引方面,数字列表现更好,分配的标识符是唯一逻辑键(社会安全号码)或唯一标识符(例如 UUID)。使用应用程序级别的唯一分配标识符很复杂,因为您可能有多个应用程序节点(在一个集群中),或者您希望同步来自应用程序内部和外部源(数据库客户端实用程序)的插入。

对于数据库分配的标识符,您需要考虑您的选择如何影响刷新。 Hibernate 尝试推迟Persistence Context flushing up until the last possible moment。这种策略传统上被称为事务性后写。

write-behind 与 Hibernate 刷新有关,而不是任何逻辑或物理事务。在事务期间,刷新可能会发生多次。

刷新的更改仅对当前数据库事务可见。在提交当前事务之前,其他并发事务看不到任何更改。

IDENTITY requires flushing,虽然序列是非事务性的,因此不需要刷新。 IDENTITY 禁用 JDBC 插入批处理,并且不支持预分配。

JPA 无法在实体构造时分配标识符,因为新实例只能通过 EntityManager.persist() 调用来持久化。 JPA 需要明确的“实体状态转换”。

浪费序列标识符不是什么大问题。即使序列值存在间隙,数据库也能正常运行。使用 bigint 列可以保证您实际上不会用完序列标识符。与具有较高死锁争用风险的事务性分配相比,具有偶尔间隙的非事务性序列标识符分配更好。

【讨论】:

我还是不明白。对于由序列支持的数字类型,如果您预取序列值,您仍然可以获得批量写入,您仍然可以获得良好的索引,您不需要刷新。在我有限的理解中,我认为没有必要将持久化和实体绑定到为其分配一个 ID,所以我不明白为什么不在构建时将先前保留的序列号分配为 ID?特别是如果浪费不是问题。 所以,我会使用预取的序列值而不是 UUID,在我看来,在没有性能和空间问题的情况下,我得到了 UUID 的优点(减去客户端上的 ID 生成),但我没有找到一个现成的解决方案(生成器),这让我感到惊讶。 这很有趣!我不知道那件事。但是它会让我在调用 persist() 之前得到一个 id,这是我的设计简单性问题(我理解以其他方式解决的性能问题)? 更好地对 UUID 和 pooled-lo 进行基准测试,看看哪个选项更适合您的需求。

以上是关于jpa序列ID生成的主要内容,如果未能解决你的问题,请参考以下文章

JPA中的实体序列生成器

Hibernate JPA 序列(非 Id)

JPA 序列生成器说增量大小与 DB 中的值不同

如何使用 JPA 从 1 自动生成 id?

JPA - 在persist()之后返回自动生成的ID

Hibernate通过将前一个递增100来生成下一个序列号