Hibernate使用PostgreSQL序列不会影响序列表。

31

我已经配置了Hibernate,使用注解来使用PostgreSQL序列生成主键的值,配置如下:

@Id 
@SequenceGenerator(name="pk_sequence",sequenceName="entity_id_seq")
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="pk_sequence")
@Column(name="id", unique=true, nullable=false)
public int getId() {
    return this.id;
}

根据这个配置,我发现当持久化时Hibernate已经分配了id值大于3000的值,而使用序列查询显示如下:

database=# select last_value from entity_id_seq;
last_value 
------------
     69

(1 行)

问题:
是否有任何错误?
Hibernate 是否应该与序列表同步?
如果不是,它在哪里存储最后生成的 ID?

谢谢。

4个回答

44

我曾经遇到过同样的问题。它与Hibernate的ID分配策略有关。当你选择GenerationType.SEQUENCE时,Hibernate使用HiLo策略,默认情况下每个块分配50个ID。因此,你可以像这样明确设置allocationSize值:

@Id 
@SequenceGenerator(name="pk_sequence",sequenceName="entity_id_seq", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="pk_sequence")
@Column(name="id", unique=true, nullable=false)
public int getId() {
    return this.id;
}

虽然我也听到过一些意见,认为在allocationSize=1的情况下使用HiLo策略并不是一个好的做法。有些人建议在处理数据库管理序列时改用GenerationType.AUTO

更新: 我最终选择了allocationSize=1,现在似乎一切都按照我的预期工作。我的应用程序本身并不需要ID块,所以YMMV


谢谢你的提醒!我一开始就注意到了allocationSize,但是认为它与调用次数有关,并且假设序列应该可以使用任何值。再次感谢! - forker
我认为这是由于Hibernate的性能原因而完成的(减少与数据库的往返次数)。如果您在序列本身上遇到争用,并且分配大小较低(nextval()调用缓慢),则可以考虑在PostgreSQL中为序列本身设置缓存。 - user330315
设置分配大小为1会影响JDBC批处理吗? - cdalxndr

32

不要在Postgres序列中使用GenerationType.SEQUENCE!

这真是令人费解,但Hibernate开发人员完全搞砸了。如果你需要重新启动/重建数据库,你必须使用GenerationType.AUTO,否则Hibernate会破坏你的序列。他们竟然允许这种代码进入生产环境几乎是一种过失疏忽。但Hibernate团队在坚持错误观点方面非常有名(例如查看他们对于LEFT JOIN的立场)。


3
好观点,马特!但我认为使用GenerationType.SEQUENCE有一个借口:如果你有一个从另一个带有序列化PK的表继承而来的表,因此与其共享一个序列,那么GenerationType.AUTO将无法为序列化PK分配正确的值(它会遇到重复的值)。 - forker
10
我强烈不同意。你只需简单地指定一个SequenceGenerator,并(如果你希望与非Hibernate应用程序进行互操作)将allocationSize设置为1。在较新的版本中,你可以使用hibernate.id.new_generator_mappings=true来恢复正常;当你编写这段代码时,也许这个选项还不存在,但allocationSize选项肯定是存在的。 - Craig Ringer
1
一些外部信息来支持你的说法会很有帮助,比如错误报告。 - cdalxndr

10

首先,您需要确定正在使用的Hibernate版本。在hibernate-core版本方面,从3.2版本开始,特别是在注释中定义的id生成器方面,引入了更加一致的支持。请参见http://in.relation.to/Bloggers/New323HibernateIdentifierGenerators进行讨论。

接下来,3.6版本引入了一个设置项('hibernate.id.new_generator_mappings'),使得在处理JPA注释时,该博客中讨论的生成器成为默认方式。该设置默认值为false,因为Hibernate必须保持与旧版本的向后兼容性。如果您想要新行为(这是完全推荐的),只需将该设置设置为true即可。

GenerationType的处理取决于您使用的版本以及您是否已将“hibernate.id.new_generator_mappings”设置为true。 我将假设您正在使用3.6+版本(因为旧版本太旧了)并且确实已将“hibernate.id.new_generator_mappings”设置为true(因为这是新应用程序的推荐设置):

  1. GenerationType.AUTO -> 被视为GenerationType.SEQUENCE
  2. GenerationType.SEQUENCE -> 映射到博客中讨论的org.hibernate.id.enhanced.SequenceStyleGenerator类
  3. GenerationType.TABLE -> 映射到博客中讨论的org.hibernate.id.enhanced.TableGenerator类

0
在Postgres中,我会这样做:
@Id 
@SequenceGenerator(name="pk_sequence",sequenceName="\"entity_id_seq\"")
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="\"pk_sequence\"")
@Column(name="\"id\"", unique=true)
private int id;

大多数大写名称需要通过转义引号传递给Hibernate,以便理解Postgres并查找表、列或序列名称。


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