当使用序列时,Hibernate会生成负的id值

53

我有一个类,其定义如下:

@Id
@SequenceGenerator(name = "SEQ_ACE_WORKERS_QUEUE_STATS_ID", sequenceName = "SEQ_ACE_WORKERS_QUEUE_STATS_ID", allocationSize = 500)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ACE_WORKERS_QUEUE_STATS_ID")
@Column(name = "ID")
private long Id;

我们在Jboss 4.2.3上运行它时,它能够正常工作并生成正确的ID(从1000+开始)。

现在我们迁移到了jboss 7.1.1,并且它生成了负数的ID!(从-498开始递增)

有任何想法为什么会发生这种情况吗?


1
你有检查过Oracle中的当前序列并跟踪Hibernate输出吗?如果你看到了正确的“select next sequence”查询,并将其复制粘贴到SQLPlus中,你得到的结果是正确/相同/预期的吗? - HRgiger
6个回答

95
新的行为如下所示: AllocationSize 是供 Hibernate 保留的主键值范围。只有在 Hibernate 使用完这个主键值范围之后,才会执行从 dual 中选择 seq.nextval
因此,在 Hibernate 的 allocationSize 和 DB 序列的 increment by 上必须声明相同的值。
当明确设置 allocationSize=500,例如在 Oracle 上时。
create sequence SEQ_ACE_WORKERS_QUEUE_STATS_ID
       MINVALUE 1 
       MAXVALUE 999999999999999999999999999 
       START WITH 1
       INCREMENT BY 500 
       NOCACHE 
       NOCYCLE;
否则,您将会注意到由于主键冲突而导致从数据库返回负值或约束错误。
当应用服务器重新启动时,您将会注意到最新分配的主键和重新启动后选择的“新”序列号之间的“跳跃”。
最终评论:默认值为50。因此,如果您在Hibernate端没有指定allocationSize,则在数据库端必须声明increment by 50。

6
非常有用的回答,因为这些信息显然没有在Hibernate文档中。 - Markus Pscheidt
这对我帮助很大。我无法让@SequenceGenerator工作,因为我一直得到负值和约束错误。只需添加allocationSize = 1即可解决问题。我正在使用PostgreSQL。 - MrtN

38

在从JBoss 6.1迁移到JBoss 7.1时,我遇到了这个问题。

根据JBoss AS 7.1 JPA文档 (https://docs.jboss.org/author/display/AS71/JPA+Reference+Guide#JPAReferenceGuide-Persistenceunitproperties),

JBoss 7.1自动设置了几个hibernate属性。其中一个属性是hibernate.id.new_generator_mappings,它激活使用不同算法并且不向后兼容的新ID生成器。将此属性设置为false在您的persistence.xml文件中将恢复旧的ID生成器行为。

hibernate 4文档还提供了有关新ID生成器的信息:http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html_single/#mapping-declaration-id-generator

hibernate文档明确说明新的ID生成器默认情况下未启用,但如上所述,JBoss 7.1会自动启用它们。


谢谢你的回答,我已经找到了,忘记在这里更新了 :( - Tomer
2
从Hibernate 5.0版本开始,hibernate.id.new_generator_mappings属性默认为true。参见http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators - Markus Pscheidt

27

将我的persistence.xml中的hibernate.id.new_generator_mappings设置为false只是解决问题的第一步:

要完全解决问题,我在@SequenceGenerator中添加了allocationSize(我之前没有添加),并将其设置为1


但是你为什么要同时添加两个呢?除了allocationSize更改之外,new_generator_mappings的哪一部分引起了问题,并通过将其设置为false来解决了问题? - Spork
将allocationSize设置为false后,它就起作用了。 - falsarella
如果您将allocationSize = 1,则不确定序列的意义。 - Aleksandr Savvopulo

1
我们遇到了类似的错误,但原因可能不同:
我们没有明确定义`allocationSize`(因此具有默认值`50`)。我们的应用程序一直在运行,然后删除了数据库并重新创建。Hibernate保留了它的ID缓存,使用了所有ID,并与数据库通信以获取新ID。
现在,Hibernate-ID缓存至少为50(对于某些表可能要高得多),但数据库已被删除、重建,目前为空。所以已经存在一些不一致性...但出于某些原因,Hibernate之后开始将下一批ID设置为-48。
我们遇到了一个后续错误,因为我们的实体使用数据类型`int`而不是`Integer`来定义ID。当Hibernate继续计数并达到ID为0的实体时,它认为该实体尚未存在于数据库中,试图进行`INSERT`而不是`UPDATE`,从而导致键验证错误。
我仍然无法解释为什么Hibernate决定在遇到这种不一致性时以-48开始键 ... 但对于我们来说,可以通过在更改数据库中的序列之前关闭应用程序来解决该错误。此外,我们所有的实体现在都使用`Integer`而不是`int`作为ID。
希望这些信息对某人有所帮助。

0

谢谢!!! 添加以下代码后运行良好

allocationSize = 1

0
在你的实体中使用策略SEQUENCE:@GeneratedValue(strategy = GenerationType.SEQUENCE) 并在数据库中创建增长为50的序列,如下例所示:create sequence <sequence> increment by 50;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接