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

Posted

技术标签:

【中文标题】Hibernate使用PostgreSQL序列不影响序列表【英文标题】:Hibernate use of PostgreSQL sequence does not affect sequence table 【发布时间】:2011-05-16 09:09:42 【问题描述】:

我已将 Hibernate 配置为使用 PostgreSQL 序列(通过注释)来为主键 id 列生成值,如下所示:

@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存放在哪里?

谢谢。

【问题讨论】:

【参考方案1】:

我遇到了同样的问题。这与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;

不过,我也听说过将 HiLo 策略与 allocationSize=1 结合使用并不是一个好的做法。当您必须处理数据库管理的序列时,有些人建议使用 GenerationType.AUTO

更新:我确实最终选择了 allocationSize=1,而且现在似乎一切正常。我的应用程序是这样的,反正我真的不需要 ID 块,所以YMMV。

【讨论】:

谢谢介意的兄弟!我从一开始就注意到了allocationSize,但认为它与调用次数有关,并假设该序列对于任何值都应该是可以的。再次感谢! 出于性能原因(减少到数据库的往返次数),我假设这是由 Hibernate 完成的。如果您在分配大小较低(nextval() 调用较慢)时遇到序列本身的争用,您可以考虑在 PostgreSQL 中为序列本身设置缓存 设置分配大小1不影响jdbc批处理吗?【参考方案2】:

不要对 Postgres 序列使用 GenerationType.SEQUENCE!

这完全违反直觉,但 Hibernate 的人完全搞砸了。您必须使用 GenerationType.AUTO,否则如果您必须重新启动/重建数据库,Hibernate 将拆除您的序列。他们允许这段代码进入生产构建几乎是犯罪过失,但 Hibernate 团队以他们对完全错误位置的牛头式立场而闻名(例如,查看他们在 LEFT JOIN 上的位置)。

【讨论】:

好点,马特!但我认为使用 GenerationType.SEQUENCE 是有理由的:如果您有一个从另一个继承了序列 pk 的表继承的表,因此为其共享一个序列,GenerationType.AUTO 将无法为序列 pk 分配正确的值(它将遇到重复值)。 我强烈反对。您必须简单地指定 SequenceGenerator 来使用它,并且(如果您希望与非 Hibernate 应用程序互操作)将 allocationSize 设置为 1。在较新的版本中,您可以改用 hibernate.id.new_generator_mappings=true 来恢复理智;当你写这篇文章时,这可能不是真的,但 allocationSize 选项肯定是。 一些支持您的声明的外部信息会有所帮助,例如错误报告。【参考方案3】:

首先,您必须确定您使用的是哪个版本的 Hibernate。就 hibernate-core 版本而言,3.2 及更高版本引入了对 id 生成器的更一致的支持,尤其是在注释中定义的方面。请参阅http://in.relation.to/Bloggers/New323HibernateIdentifierGenerators 进行讨论。

Next 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(因为这是对新应用的建议):

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

【讨论】:

【参考方案4】:

在 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 并找到表、列或序列名称。

【讨论】:

以上是关于Hibernate使用PostgreSQL序列不影响序列表的主要内容,如果未能解决你的问题,请参考以下文章

使用自动创建表时,Hibernate 不会自动为数据库创建序列

缺少序列或表:hibernate_sequence

Hibernate 5 ID AUTO 生成类型为 Oracle 作为序列和 MySQL 作为身份

如何使用 JPA 和 Hibernate 映射 PostgreSQL 枚举

使用 PostgreSQL 的 Hibernate import.sql 重复主键

使用 Hibernate 从 PostgreSQL 中的 JSONArray 获取对象的问题