2014-11-21 188 views
3

在将实体ID持久化到数据库之前,但在构造函数中立即为实体分配ID会有巨大的价值:您的equals/hashcode实现变得微不足道,and it saves many headachesjpa序列ID的生成

当实体等同性基于==时,我看到了问题:代理进入会话,并且当它作为真实对象被解包时,将得到equals(),返回false

而且当你重写equalshashcode使用生成的ID,因为仅在persist()产生的一个,所有非持续的实体有ID null,因此都彼此相等。

从我读到的内容来看,当您使用传统的ID生成技术(自动递增)时,ID会在实体管理器刷新时生成。当您使用基于序列的解决方案时,它会持续生成。

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

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

我错过了什么?是否有可能在某处某个JPA身份生成器或某个库将基于序列并允许在施工时提供该ID?

回答

2

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

当涉及到读取和索引时,数字列执行得更好,并且分配的标识符是唯一逻辑密钥(社会安全号码)或唯一标识符(例如UUID)。使用应用程序级唯一分配的标识符非常复杂,因为您可能有多个应用程序节点(在集群中),或者希望在应用程序内以及从外部源(数据库客户端实用程序)同时插入同步插入。

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

后写与Hibernate刷新更多地相关,而不是任何逻辑或物理事务。在交易过程中,刷新可能会发生多次。

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

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

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

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

+0

我还是不明白。对于由序列支持的数字类型,如果预取序列值,您仍然可以批量写入,但仍然可以获得良好的索引,不需要刷新。在我有限的理解中,我没有看到需要将持久化和实体分配给一个ID,所以我不明白为什么不在建造时分配一个先前保留的序列号作为ID?特别是如果浪费不是问题。 – 2014-11-21 15:12:30

+0

所以,我会使用prefetched序列值,而不是UUID,在我看来,我得到的UUID(在客户端减ID生成)没有性能和空间的担忧,但我没有找到一个现成的解决方案(发电机),这让我感到惊讶。 – 2014-11-21 15:19:24

+1

我建议你开始使用[pooled-lo optimizer](http://vladmihalcea.com/2014/07/21/hibernate-hidden-gem-the-pooled-lo-optimizer/)以及序列生成器。点击此链接查看如何做到这一点。 – 2014-11-21 15:36:30